list.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. <template>
  2. <view>
  3. <view class="uni-header">
  4. <view class="uni-group">
  5. <uni-stat-breadcrumb />
  6. </view>
  7. <view class="uni-group">
  8. <button class="uni-button" type="default" size="mini" @click="search">搜索</button>
  9. <download-excel class="hide-on-phone" :fields="exportExcel.fields" :data="exportExcelData" :type="exportExcel.type" :name="exportExcel.filename">
  10. <button class="uni-button" type="primary" size="mini">导出 Excel</button>
  11. </download-excel>
  12. </view>
  13. </view>
  14. <view class="uni-container">
  15. <view class="uni-stat--x">
  16. <uni-stat-tabs label="平台选择" type="boldLine" mode="platform" v-model="query.platform" @change="platformChange" />
  17. </view>
  18. <unicloud-db ref="udb" :collection="collectionList" field="user_id,nickname,provider,provider_pay_type,uni_platform,status,type,order_no,out_trade_no,transaction_id,device_id,client_ip,openid,description,err_msg,total_fee,refund_fee,refund_count,refund_list,provider_appid,appid,user_order_success,create_date,pay_date,notify_date,cancel_date" :where="where" page-data="replace"
  19. :orderby="orderby" :getcount="true" :page-size="options.pageSize" :page-current="options.pageCurrent"
  20. v-slot:default="{data,pagination,loading,error,options}" :options="options" loadtime="manual" @load="onqueryload">
  21. <uni-table ref="table" :loading="loading" :emptyText="error.message || loading ? '请求中...' : '没有更多数据'" border stripe type="" @selection-change="selectionChange" style="min-height: 900px;">
  22. <uni-tr>
  23. <uni-th align="center">序号</uni-th>
  24. <uni-th ref="user_id" align="center" :filterDefaultValue="filterDefaultValueUserId" filter-type="search" @filter-change="filterChange($event, 'user_id')" sortable @sort-change="sortChange($event, 'user_id')">用户</uni-th>
  25. <uni-th align="center" filter-type="select" :filter-data="options.filterData.provider_localdata" @filter-change="filterChange($event, 'provider')">支付供应商</uni-th>
  26. <uni-th align="center" filter-type="search" @filter-change="filterChange($event, 'provider_pay_type')" sortable @sort-change="sortChange($event, 'provider_pay_type')">支付方式</uni-th>
  27. <uni-th align="center" filter-type="search" @filter-change="filterChange($event, 'uni_platform')" sortable @sort-change="sortChange($event, 'uni_platform')">应用平台</uni-th>
  28. <uni-th align="center" filter-type="select" :filter-data="options.filterData.status_localdata" @filter-change="filterChange($event, 'status')">订单状态</uni-th>
  29. <uni-th align="center" filter-type="search" @filter-change="filterChange($event, 'type')" sortable @sort-change="sortChange($event, 'type')">订单类型</uni-th>
  30. <uni-th align="center" filter-type="search" @filter-change="filterChange($event, 'order_no')" sortable @sort-change="sortChange($event, 'order_no')">业务系统订单号</uni-th>
  31. <uni-th align="center" filter-type="search" @filter-change="filterChange($event, 'out_trade_no')" sortable @sort-change="sortChange($event, 'out_trade_no')">支付插件订单号</uni-th>
  32. <uni-th align="center" filter-type="search" @filter-change="filterChange($event, 'transaction_id')" sortable @sort-change="sortChange($event, 'transaction_id')">交易单号</uni-th>
  33. <uni-th align="center" filter-type="search" @filter-change="filterChange($event, 'description')" sortable @sort-change="sortChange($event, 'description')">支付描述</uni-th>
  34. <uni-th align="center" filter-type="range" @filter-change="filterChange($event, 'total_fee', 0.01)" sortable @sort-change="sortChange($event, 'total_fee')">订单支付金额</uni-th>
  35. <uni-th align="center" filter-type="range" @filter-change="filterChange($event, 'refund_fee', 0.01)" sortable @sort-change="sortChange($event, 'refund_fee')">订单退款金额</uni-th>
  36. <uni-th align="center" filter-type="range" @filter-change="filterChange($event, 'refund_count')" sortable @sort-change="sortChange($event, 'refund_count')">当前退款笔数</uni-th>
  37. <uni-th align="center" sortable @sort-change="sortChange($event, 'user_order_success')">回调状态</uni-th>
  38. <uni-th align="center" filter-type="timestamp" @filter-change="filterChange($event, 'create_date')" sortable @sort-change="sortChange($event, 'create_date')">创建时间</uni-th>
  39. <uni-th align="center" filter-type="timestamp" @filter-change="filterChange($event, 'pay_date')" sortable @sort-change="sortChange($event, 'pay_date')">支付时间</uni-th>
  40. <uni-th align="center" filter-type="timestamp" @filter-change="filterChange($event, 'cancel_date')" sortable @sort-change="sortChange($event, 'cancel_date')">取消时间</uni-th>
  41. <uni-th align="center" filter-type="search" @filter-change="filterChange($event, 'provider_appid')" sortable @sort-change="sortChange($event, 'provider_appid')">开放平台appid</uni-th>
  42. <uni-th align="center" filter-type="search" @filter-change="filterChange($event, 'appid')" sortable @sort-change="sortChange($event, 'appid')">DCloud AppId</uni-th>
  43. <uni-th align="center" filter-type="search" @filter-change="filterChange($event, 'device_id')" sortable @sort-change="sortChange($event, 'device_id')">设备ID</uni-th>
  44. <uni-th align="center" filter-type="search" @filter-change="filterChange($event, 'client_ip')" sortable @sort-change="sortChange($event, 'client_ip')">客户端IP</uni-th>
  45. <uni-th align="center" filter-type="search" @filter-change="filterChange($event, 'openid')" sortable @sort-change="sortChange($event, 'openid')">openid</uni-th>
  46. </uni-tr>
  47. <uni-tr v-for="(item,index) in data" :key="index">
  48. <uni-td align="center">{{ parseInt((index+1) + (pagination.current-1) * pagination.size) }} </uni-td>
  49. <uni-td align="center"><text class="text-btn" @click="pageToUser(item)">{{ nameFormat(item) }}</text> </uni-td>
  50. <uni-td align="center">{{options.provider_valuetotext[item.provider]}}</uni-td>
  51. <uni-td align="center">{{item.provider_pay_type}}</uni-td>
  52. <uni-td align="center">{{item.uni_platform}}</uni-td>
  53. <uni-td align="center">{{options.status_valuetotext[item.status]}}</uni-td>
  54. <uni-td align="center">{{item.type}}</uni-td>
  55. <uni-td align="center">{{item.order_no}}</uni-td>
  56. <uni-td align="center">{{item.out_trade_no}}</uni-td>
  57. <uni-td align="center">{{item.transaction_id}}</uni-td>
  58. <uni-td align="center">{{item.description}}</uni-td>
  59. <uni-td align="center">{{ (item.total_fee * 0.01).toFixed(2) }}</uni-td>
  60. <uni-td align="center">{{ (item.refund_fee * 0.01).toFixed(2) }}</uni-td>
  61. <uni-td align="center">{{item.refund_count}}</uni-td>
  62. <uni-td align="center">
  63. <view v-if="item.user_order_success === true" style="color:#18bc37">✔正常</view>
  64. <view v-else-if="[-1,0].indexOf(item.status) > -1 ">-</view>
  65. <view v-else style="color:#e43d33">●异常</view>
  66. </uni-td>
  67. <uni-td align="center">
  68. <uni-dateformat :threshold="[0, 0]" :date="item.create_date"></uni-dateformat>
  69. </uni-td>
  70. <uni-td align="center">
  71. <uni-dateformat :threshold="[0, 0]" :date="item.pay_date"></uni-dateformat>
  72. </uni-td>
  73. <uni-td align="center">
  74. <uni-dateformat :threshold="[0, 0]" :date="item.cancel_date"></uni-dateformat>
  75. </uni-td>
  76. <uni-td align="center">{{item.provider_appid}}</uni-td>
  77. <uni-td align="center">{{item.appid}}</uni-td>
  78. <uni-td align="center">{{item.device_id}}</uni-td>
  79. <uni-td align="center">{{item.client_ip}}</uni-td>
  80. <uni-td align="center">{{item.openid}}</uni-td>
  81. </uni-tr>
  82. </uni-table>
  83. <view class="uni-pagination-box">
  84. <uni-pagination show-icon :page-size="pagination.size" v-model="pagination.current" :total="pagination.count" @change="onPageChanged" />
  85. </view>
  86. </unicloud-db>
  87. </view>
  88. <uni-popup ref="popup" type="center" :animation="false">
  89. <view style="padding: 30px;background-color: #ffffff;width: 500px;">
  90. <view style="margin-bottom: 20px;text-align: center;font-size: 20px;font-weight: bold;">退款确认</view>
  91. <uni-forms ref="refundForm" :modelValue="refundFormData" label-position="left" labelWidth="100px" :rules="refundFormRules">
  92. <uni-forms-item label="退款金额" name="refund_fee">
  93. <uni-easyinput type="text" v-model.number="refundFormData.refund_fee" placeholder="请输入退款金额" :clearable="false" ></uni-easyinput>
  94. <view style="color: #666;margin-top: 5px;font-size: 12px;">最大可退:{{ refundFormData.max_refund_fee }}</view>
  95. </uni-forms-item>
  96. <uni-forms-item label="退款原因" name="refund_desc">
  97. <uni-easyinput type="textarea" v-model="refundFormData.refund_desc" placeholder="请输入退款原因" :clearable="false" />
  98. </uni-forms-item>
  99. <button type="warn" style="width: 100px;height: 40px;font-size: 16px;" @click="confirmRefund(refundFormData);">确定</button>
  100. </uni-forms>
  101. </view>
  102. </uni-popup>
  103. </view>
  104. </template>
  105. <script>
  106. import { enumConverter, filterToWhere } from '../../../../js_sdk/validator/uni-pay-orders.js';
  107. // 引入支付云对象
  108. const uniPayCo = uniCloud.importObject("uni-pay-co");
  109. const db = uniCloud.database();
  110. // 表查询配置
  111. const dbOrderBy = 'create_date desc'; // 排序字段
  112. const dbSearchFields = ['order_no', 'out_trade_no', 'transaction_id']; // 模糊搜索字段,支持模糊搜索的字段列表。联表查询格式: 主表字段名.副表字段名,例如用户表关联角色表 role.role_name
  113. // 分页配置
  114. const pageSize = 20;
  115. const pageCurrent = 1;
  116. const orderByMapping = {
  117. "ascending": "asc",
  118. "descending": "desc"
  119. };
  120. import {
  121. mapfields,
  122. stringifyQuery,
  123. stringifyField,
  124. stringifyGroupField,
  125. getTimeOfSomeDayAgo,
  126. division,
  127. format,
  128. formatDate,
  129. parseDateTime,
  130. getFieldTotal,
  131. debounce
  132. } from '@/js_sdk/uni-stat/util.js'
  133. export default {
  134. data() {
  135. return {
  136. collectionList: "uni-pay-orders",
  137. query: {
  138. appid: '',
  139. platform_id: '',
  140. uni_platform: '',
  141. version: '',
  142. pay_date: [],
  143. },
  144. where: '',
  145. orderby: dbOrderBy,
  146. orderByFieldName: "",
  147. selectedIndexs: [],
  148. filterDefaultValueUserId: "",
  149. refundFormData:{
  150. out_trade_no: "",
  151. max_refund_fee: "",
  152. refund_fee:"",
  153. refund_desc: ""
  154. },
  155. refundFormRules:{
  156. refund_fee: {
  157. rules:[
  158. // 校验 name 不能为空
  159. { required: true,errorMessage: '退款金额必须>0' },
  160. // 对name字段进行长度验证
  161. {
  162. minimum: 0.01,
  163. maximum: 0,
  164. errorMessage: '最大可退 {maximum} 元',
  165. }
  166. ],
  167. },
  168. refund_desc: {
  169. rules:[
  170. // 校验 name 不能为空
  171. { required: true,errorMessage: '请输入退款原因' },
  172. ],
  173. }
  174. },
  175. options: {
  176. pageSize,
  177. pageCurrent,
  178. filterData: {
  179. "provider_localdata": [{
  180. "text": "微信支付",
  181. "value": "wxpay"
  182. },
  183. {
  184. "text": "支付宝",
  185. "value": "alipay"
  186. },
  187. {
  188. "text": "苹果应用内支付",
  189. "value": "appleiap"
  190. }
  191. ],
  192. "status_localdata": [{
  193. "text": "已关闭",
  194. "value": -1
  195. },
  196. {
  197. "text": "未支付",
  198. "value": 0
  199. },
  200. {
  201. "text": "已支付",
  202. "value": 1
  203. },
  204. {
  205. "text": "已部分退款",
  206. "value": 2
  207. },
  208. {
  209. "text": "已全额退款",
  210. "value": 3
  211. }
  212. ]
  213. },
  214. ...enumConverter
  215. },
  216. imageStyles: {
  217. width: 64,
  218. height: 64
  219. },
  220. exportExcel: {
  221. "filename": "uni-pay-orders.xls",
  222. "type": "xls",
  223. "fields": {
  224. "用户ID": "user_id",
  225. "用户昵称": "nickname",
  226. "支付供应商": "provider",
  227. "支付方式": "provider_pay_type",
  228. "应用平台": "uni_platform",
  229. "订单状态": "status",
  230. "支付失败原因": "err_msg",
  231. "订单类型": "type",
  232. "业务系统订单号": "order_no",
  233. "支付插件订单号": "out_trade_no",
  234. "交易单号": "transaction_id",
  235. "支付描述": "description",
  236. "订单支付金额": "total_fee",
  237. "订单退款金额": "refund_fee",
  238. "当前退款笔数": "refund_count",
  239. "退款详情": "refund_list",
  240. "回调状态": "user_order_success",
  241. "创建时间": "create_date",
  242. "支付时间": "pay_date",
  243. "异步通知时间": "notify_date",
  244. "取消时间": "cancel_date",
  245. "开放平台appid": "provider_appid",
  246. "DCloud AppId": "appid",
  247. "设备ID": "device_id",
  248. "客户端IP": "client_ip",
  249. "openid": "openid",
  250. }
  251. },
  252. exportExcelData: []
  253. }
  254. },
  255. onLoad(e) {
  256. this._filter = {}
  257. if (e.user_id) {
  258. this.filterDefaultValueUserId = e.user_id;
  259. this.filterChange({
  260. filterType: "search",
  261. filter: e.user_id,
  262. }, "user_id");
  263. }
  264. },
  265. onReady() {
  266. this.$refs.udb.loadData()
  267. },
  268. methods: {
  269. onqueryload(data) {
  270. this.exportExcelData = data
  271. },
  272. getWhere() {
  273. let where = "";
  274. let {
  275. pay_date,
  276. appid,
  277. version,
  278. uni_platform,
  279. //query, // 模糊查询
  280. } = this.query;
  281. if (pay_date && pay_date.length == 2) {
  282. where += ` && pay_date>=${pay_date[0]} && pay_date<=${pay_date[1]}`;
  283. }
  284. if (appid) {
  285. where += ` && appid=='${appid}'`;
  286. }
  287. if (version) {
  288. where += ` && stat_data.app_version=='${version}'`;
  289. }
  290. if (uni_platform) {
  291. where += ` && stat_data.platform=='${uni_platform}'`;
  292. }
  293. // if (query) {
  294. // const queryRe = new RegExp(query, 'i');
  295. // let queryReStr = dbSearchFields.map(name => queryRe + '.test(' + name + ')').join(' || ');
  296. // where += ` && (${queryReStr})`;
  297. // }
  298. where = where.substring(3).trim();
  299. // console.log('where: ', where)
  300. return where;
  301. },
  302. search() {
  303. const newWhere = this.getWhere()
  304. this.where = newWhere
  305. this.$nextTick(() => {
  306. this.loadData()
  307. })
  308. },
  309. loadData(clear = true) {
  310. this.$refs.udb.loadData({
  311. clear
  312. })
  313. },
  314. onPageChanged(e) {
  315. this.selectedIndexs.length = 0
  316. this.$refs.table.clearSelection()
  317. this.$refs.udb.loadData({
  318. current: e.current
  319. })
  320. },
  321. navigateTo(url, clear) {
  322. // clear 表示刷新列表时是否清除页码,true 表示刷新并回到列表第 1 页,默认为 true
  323. uni.navigateTo({
  324. url,
  325. events: {
  326. refreshData: () => {
  327. this.loadData(clear)
  328. }
  329. }
  330. })
  331. },
  332. // 多选处理
  333. selectedItems() {
  334. let dataList = this.$refs.udb.dataList
  335. return this.selectedIndexs.map(i => dataList[i]._id)
  336. },
  337. // 批量删除
  338. delTable() {
  339. this.$refs.udb.remove(this.selectedItems(), {
  340. success: (res) => {
  341. this.$refs.table.clearSelection()
  342. }
  343. })
  344. },
  345. // 多选
  346. selectionChange(e) {
  347. this.selectedIndexs = e.detail.index
  348. },
  349. refundPopup(key, item){
  350. if (key) {
  351. let {
  352. total_fee = 0,
  353. refund_fee = 0,
  354. out_trade_no
  355. } = item;
  356. let max_refund_fee = Number(((total_fee - refund_fee) / 100).toFixed(2));
  357. this.refundFormData.max_refund_fee = max_refund_fee;
  358. this.refundFormData.refund_fee = max_refund_fee;
  359. this.refundFormData.out_trade_no = out_trade_no;
  360. this.refundFormRules.refund_fee.rules[1].maximum = max_refund_fee;
  361. this.$refs.popup.open();
  362. } else {
  363. this.refundFormData.max_refund_fee = "";
  364. this.refundFormData.refund_fee = "";
  365. this.refundFormData.out_trade_no = "";
  366. this.refundFormRules.refund_fee.rules[1].maximum = 0;
  367. this.$refs.popup.close();
  368. }
  369. },
  370. // 主动退款
  371. async confirmRefund(item) {
  372. let {
  373. total_fee = 0,
  374. refund_fee = 0,
  375. out_trade_no,
  376. refund_desc
  377. } = item;
  378. item.refund_fee = Number((item.refund_fee).toFixed(2));
  379. this.$refs.refundForm.validate().then(async formData=>{
  380. //console.log('表单数据信息:', formData);
  381. let apply_refund_fee = Number(refund_fee);
  382. if (isNaN(apply_refund_fee) || apply_refund_fee <= 0) {
  383. uni.showToast({
  384. title: "请输入正确的退款金额",
  385. icon: 'none',
  386. success: () => {
  387. setTimeout(() => {
  388. this.confirmRefund(item);
  389. }, 500);
  390. }
  391. })
  392. return;
  393. }
  394. let refundData = {
  395. out_trade_no,
  396. refund_fee: parseInt(apply_refund_fee * 100), // 金额已分为单位,100 = 1元
  397. refund_desc,
  398. };
  399. //console.log('refundData: ', refundData)
  400. let res = await uniPayCo.refund(refundData);
  401. if (!res.errCode) {
  402. this.refundPopup(false);
  403. this.loadData(false);
  404. }
  405. }).catch(err =>{
  406. //console.log('表单错误信息:', err);
  407. });
  408. },
  409. sortChange(e, name) {
  410. this.orderByFieldName = name;
  411. if (e.order) {
  412. this.orderby = name + ' ' + orderByMapping[e.order]
  413. } else {
  414. this.orderby = ''
  415. }
  416. this.$refs.table.clearSelection()
  417. this.$nextTick(() => {
  418. this.$refs.udb.loadData()
  419. })
  420. },
  421. filterChange(e, name, k) {
  422. if (k && e.filter) {
  423. if (typeof e.filter == "object") {
  424. if (typeof e.filter[0] === "number") e.filter[0] = e.filter[0] / k;
  425. if (typeof e.filter[1] === "number") e.filter[1] = e.filter[1] / k;
  426. }
  427. }
  428. this._filter[name] = {
  429. type: e.filterType,
  430. value: e.filter,
  431. }
  432. let newWhere = filterToWhere(this._filter, db.command)
  433. if (Object.keys(newWhere).length) {
  434. this.where = newWhere
  435. } else {
  436. this.where = ''
  437. }
  438. this.$nextTick(() => {
  439. this.$refs.udb.loadData()
  440. })
  441. },
  442. platformChange(id, index, name, item) {
  443. this.query.version = 0
  444. this.query.uni_platform = item.code
  445. },
  446. nameFormat(item) {
  447. if (!item.user_id) {
  448. return "匿名用户";
  449. } else if (item.nickname) {
  450. return `${item.user_id}(${item.nickname})`
  451. } else {
  452. return item.user_id;
  453. }
  454. },
  455. pageToUser(item) {
  456. let { user_id } = item;
  457. uni.navigateTo({
  458. url: `/pages/system/user/list?id=${user_id}`
  459. });
  460. },
  461. },
  462. watch: {
  463. query: {
  464. deep: true,
  465. handler(val) {
  466. this.search()
  467. }
  468. }
  469. },
  470. computed: {
  471. versionQuery() {
  472. const {
  473. appid,
  474. uni_platform
  475. } = this.query
  476. const query = stringifyQuery({
  477. appid,
  478. uni_platform
  479. })
  480. return query
  481. }
  482. },
  483. }
  484. </script>
  485. <style lang="scss" scoped>
  486. .text-btn{
  487. color: $uni-color-primary;
  488. cursor: pointer;
  489. margin: 0 5px;
  490. }
  491. </style>