index.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  1. <template>
  2. <!-- 搜索 -->
  3. <ContentWrap>
  4. <el-form
  5. ref="queryFormRef"
  6. :model="queryParams"
  7. class="-mb-15px"
  8. label-width="68px"
  9. :inline="true"
  10. >
  11. <el-form-item label="订单状态" prop="status">
  12. <el-select class="!w-280px" v-model="queryParams.status" clearable placeholder="全部">
  13. <el-option
  14. v-for="dict in getStrDictOptions(DICT_TYPE.TRADE_ORDER_STATUS)"
  15. :key="(dict.value as string)"
  16. :label="dict.label"
  17. :value="dict.value"
  18. />
  19. </el-select>
  20. </el-form-item>
  21. <el-form-item label="支付方式" prop="payChannelCode">
  22. <el-select
  23. v-model="queryParams.payChannelCode"
  24. class="!w-280px"
  25. clearable
  26. placeholder="全部"
  27. >
  28. <el-option
  29. v-for="dict in getStrDictOptions(DICT_TYPE.PAY_CHANNEL_CODE_TYPE)"
  30. :key="(dict.value as string)"
  31. :label="dict.label"
  32. :value="dict.value"
  33. />
  34. </el-select>
  35. </el-form-item>
  36. <el-form-item label="创建时间" prop="createTime">
  37. <el-date-picker
  38. v-model="queryParams.createTime"
  39. :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
  40. class="!w-280px"
  41. start-placeholder="自定义时间"
  42. end-placeholder="自定义时间"
  43. type="daterange"
  44. value-format="YYYY-MM-DD HH:mm:ss"
  45. />
  46. </el-form-item>
  47. <el-form-item label="订单来源" prop="terminal">
  48. <el-select class="!w-280px" v-model="queryParams.terminal" clearable placeholder="全部">
  49. <el-option
  50. v-for="dict in getStrDictOptions(DICT_TYPE.TERMINAL)"
  51. :key="(dict.value as string)"
  52. :label="dict.label"
  53. :value="dict.value"
  54. />
  55. </el-select>
  56. </el-form-item>
  57. <el-form-item label="订单类型" prop="type">
  58. <el-select class="!w-280px" v-model="queryParams.type" clearable placeholder="全部">
  59. <el-option
  60. v-for="dict in getStrDictOptions(DICT_TYPE.TRADE_ORDER_TYPE)"
  61. :key="(dict.value as string)"
  62. :label="dict.label"
  63. :value="dict.value"
  64. />
  65. </el-select>
  66. </el-form-item>
  67. <el-form-item label="订单搜索">
  68. <el-input
  69. v-show="true"
  70. class="!w-280px"
  71. v-model="queryType.v"
  72. clearable
  73. placeholder="请输入"
  74. >
  75. <template #prepend>
  76. <el-select style="width: 110px" v-model="queryType.k" clearable placeholder="全部">
  77. <el-option
  78. v-for="dict in searchList"
  79. :key="dict.value"
  80. :label="dict.label"
  81. :value="dict.value"
  82. />
  83. </el-select>
  84. </template>
  85. </el-input>
  86. </el-form-item>
  87. <el-form-item>
  88. <el-button @click="handleQuery" v-hasPermi="['trade:order:query']">
  89. <Icon class="mr-5px" icon="ep:search" />
  90. 搜索
  91. </el-button>
  92. <el-button @click="resetQuery" v-hasPermi="['trade:order:query']">
  93. <Icon class="mr-5px" icon="ep:refresh" />
  94. 重置
  95. </el-button>
  96. <el-button type="success" plain @click="handleExport" :loading="exportLoading">
  97. <!-- v-hasPermi="['trade:order:export']" -->
  98. <Icon icon="ep:download" class="mr-5px" /> 导出TODO
  99. </el-button>
  100. </el-form-item>
  101. </el-form>
  102. </ContentWrap>
  103. <!-- 表格 -->
  104. <ContentWrap>
  105. <!-- 表单 -->
  106. <el-table v-loading="loading" :data="list">
  107. <el-table-column type="expand" fixed="left">
  108. <template #default="scope">
  109. <el-descriptions class="mx-40">
  110. <el-descriptions-item label="商品原价(总): ">{{
  111. '¥ ' +
  112. parseFloat((scope.row.originalPrice / 100) as unknown as string).toFixed(2) +
  113. ' 元'
  114. }}</el-descriptions-item>
  115. <el-descriptions-item label="下单时间: ">
  116. {{ formatDate(scope.row.createTime) }}</el-descriptions-item
  117. >
  118. <el-descriptions-item label="推广人: ">TODO</el-descriptions-item>
  119. <el-descriptions-item label="用户备注: ">{{
  120. scope.row.userRemark
  121. }}</el-descriptions-item>
  122. <el-descriptions-item label="商家备注: ">{{ scope.row.remark }}</el-descriptions-item>
  123. </el-descriptions>
  124. </template>
  125. </el-table-column>
  126. <el-table-column width="100" fixed="left">
  127. <template #header>
  128. <el-dropdown icon="eq:search" @command="handleDropType">
  129. <el-button link type="primary">全选({{ orderSelect.selectTotal }}) </el-button>
  130. <template #dropdown>
  131. <el-dropdown-menu>
  132. <el-dropdown-item command="1">当前页</el-dropdown-item>
  133. <el-dropdown-item command="2">所有页</el-dropdown-item>
  134. </el-dropdown-menu>
  135. </template>
  136. </el-dropdown>
  137. </template>
  138. <template #default="scope">
  139. <el-checkbox v-model="scope.row.itemSelect" @change="handcheckclick(scope.row)" />
  140. </template>
  141. </el-table-column>
  142. <el-table-column label="订单号" align="center" min-width="110">
  143. <template #default="scope">
  144. <el-button link type="primary" @click="showOrderDetail(scope.row)">{{
  145. scope.row.no
  146. }}</el-button>
  147. </template>
  148. </el-table-column>
  149. <el-table-column label="订单类型" align="center" min-width="100">
  150. <template #default="scope">
  151. <dict-tag :type="DICT_TYPE.TRADE_ORDER_TYPE" :value="scope.row.type" />
  152. </template>
  153. </el-table-column>
  154. <el-table-column label="用户信息" align="center" min-width="100">
  155. <template #default="scope">
  156. <el-button link type="primary" @click="goUserDetail(scope.row)"
  157. >{{ scope.row.userId }}{{ '[' + scope.row.user.nickname + ']' }}</el-button
  158. >
  159. </template>
  160. </el-table-column>
  161. <el-table-column
  162. :formatter="dateFormatter"
  163. align="center"
  164. label="创建时间"
  165. prop="createTime"
  166. min-width="180"
  167. />
  168. <el-table-column label="订单来源" align="center" min-width="100">
  169. <template #default="scope">
  170. <dict-tag
  171. v-if="scope.row.terminal"
  172. :type="DICT_TYPE.TERMINAL"
  173. :value="scope.row.terminal"
  174. />
  175. <span v-else>{{ scope.terminal }}</span>
  176. </template>
  177. </el-table-column>
  178. <el-table-column label="商品信息" align="left" min-width="200" prop="items">
  179. <template #default="scope">
  180. <el-popover
  181. ref="popover"
  182. placement="bottom"
  183. :title="'订单:' + scope.row.no"
  184. :width="400"
  185. trigger="hover"
  186. >
  187. <template #reference>
  188. <div>
  189. <div v-for="item in scope.row.items" :key="item">
  190. <el-image
  191. style="width: 36px; height: 36px"
  192. :src="item.picUrl"
  193. :preview-src-list="[item.picUrl]"
  194. fit="cover"
  195. @click="imagePreview(item.picUrl)"
  196. />
  197. <span class="m-2">{{ item.spuName }}</span>
  198. </div>
  199. </div>
  200. </template>
  201. <div v-for="item in scope.row.items" :key="item">
  202. <div>
  203. <p>{{ item.spuName }}</p>
  204. <p>{{
  205. '¥ ' +
  206. parseFloat((item.payPrice / 100) as unknown as string).toFixed(2) +
  207. '元 x ' +
  208. item.count
  209. }}</p>
  210. </div>
  211. </div>
  212. </el-popover>
  213. </template>
  214. </el-table-column>
  215. <el-table-column label="实际支付(元)" align="center" prop="payPrice" min-width="100">
  216. <template #default="scope">
  217. {{ '¥ ' + parseFloat((scope.row.payPrice / 100) as unknown as string).toFixed(2) }}
  218. </template>
  219. </el-table-column>
  220. <el-table-column
  221. :formatter="dateFormatter"
  222. align="center"
  223. label="支付时间"
  224. prop="payTime"
  225. min-width="180"
  226. />
  227. <el-table-column label="支付类型" align="center" min-width="100" prop="payChannelCode">
  228. <template #default="scope">
  229. <dict-tag
  230. v-if="scope.row.payChannelCode"
  231. :type="DICT_TYPE.PAY_CHANNEL_CODE_TYPE"
  232. :value="scope.row.payChannelCode"
  233. />
  234. </template>
  235. </el-table-column>
  236. <el-table-column label="订单状态" align="center" prop="status" min-width="100">
  237. <template #default="scope">
  238. <dict-tag
  239. v-if="scope.row.status !== ''"
  240. :type="DICT_TYPE.TRADE_ORDER_STATUS"
  241. :value="scope.row.status"
  242. />
  243. <span v-else>{{ scope.status }}</span>
  244. </template>
  245. </el-table-column>
  246. <el-table-column label="操作" align="center" fixed="right" min-width="150">
  247. <template #default="scope">
  248. <!-- <el-button v-if="scope.row.status == '0'" link type="primary" @click="sendXX(scope.row)"
  249. >待支付</el-button> -->
  250. <el-button v-if="scope.row.status == '10'" link type="primary" @click="sendXX(scope.row)"
  251. >发货</el-button
  252. >
  253. <el-button link type="primary" @click="showOrderDetail(scope.row)">详情</el-button>
  254. </template>
  255. </el-table-column>
  256. </el-table>
  257. <!-- 分页 -->
  258. <Pagination
  259. v-model:limit="queryParams.pageSize"
  260. v-model:page="queryParams.pageNo"
  261. :total="total"
  262. @pagination="getList"
  263. />
  264. </ContentWrap>
  265. <el-image-viewer
  266. v-if="imgViewVisible"
  267. :url-list="imageViewerList"
  268. @close="imgViewVisible = false"
  269. />
  270. </template>
  271. <script setup lang="ts" name="OrderList">
  272. import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
  273. import * as TradeOrderApi from '@/api/mall/trade/order'
  274. import {
  275. TradeOrderPageReqVO,
  276. SelectType,
  277. TradeOrderPageItemRespVO
  278. } from '@/api/mall/trade/order/type/orderType'
  279. import { dateFormatter, formatDate } from '@/utils/formatTime'
  280. import download from '@/utils/download'
  281. const message = useMessage()
  282. const { push } = useRouter()
  283. const imgViewVisible = ref(false) // 商品图预览
  284. const imageViewerList = ref<string[]>([]) // 商品图预览列表
  285. const queryFormRef = ref()
  286. const loading = ref(false)
  287. const exportLoading = ref(false)
  288. const total = ref(0) // 总记录数
  289. const list = ref<Array<TradeOrderPageItemRespVO | any>>([]) //表数据
  290. //选中状态选中处理
  291. const orderSelect: SelectType = reactive({
  292. queryParams: {} as TradeOrderPageReqVO,
  293. selectTotal: 0,
  294. selectAllFlag: false,
  295. selectData: new Map<number, Set<string>>(),
  296. unSelectList: new Set<string>()
  297. })
  298. //表单搜索
  299. const queryParams: TradeOrderPageReqVO = reactive({
  300. pageNo: 1, //首页
  301. pageSize: 10 //页面大小
  302. })
  303. const queryType = reactive({ k: '', v: '' }) // 订单搜索类型kv
  304. /*
  305. * 订单搜索
  306. * 商品名称 商品件数 全部 需要后端支持TODO
  307. */
  308. const searchList = ref([
  309. { value: 'no', label: '订单号' },
  310. { value: 'userId', label: '用户UID' },
  311. { value: 'userNickname', label: '用户昵称' },
  312. { value: 'userMobile', label: '用户电话' },
  313. { value: 'spuName', label: '商品名称TODO' },
  314. { value: 'itemCount', label: '商品件数TODO' }
  315. ])
  316. /**
  317. 当前页/? 如果pageNo存在,则将但前数据全部按照单个选中模式取消 ,不存在,则新增全页 增加 Map.pageNo Map.roderNoList
  318. 单个选中 如果pagelist存在,订单号选中状态取反,并对总数按选中状态加减。如果pagelist不存在,订单号选中状态取反,并对总数按选中状态加减,增加 Map.pageNo,
  319. 如果当前Map.pageNo 所对应list 为空 ,清除pageNo
  320. * @param command ===1 当前页 选中 ===2 所有页面选中
  321. */
  322. const handleDropType = (command: string) => {
  323. let i = 0
  324. //当前页按钮
  325. if (command === '1') {
  326. //如果该页面有选中数据 则选中事件触发时 取消该页面
  327. if (orderSelect.selectData && orderSelect.selectData.has(queryParams.pageNo)) {
  328. for (i = 0; i < list.value.length; i++) {
  329. if (orderSelect.selectData.get(queryParams.pageNo)!.has(list.value[i].id)) {
  330. //选中数量减少
  331. orderSelect.selectTotal -= 1
  332. //考虑全选中,针对某一页面选中当前页时 会将所有数据中去掉该页面, 需要登记到 orderSelect.unSelectList
  333. unSelectListRecord(list.value[i].id, 'add')
  334. }
  335. list.value[i]['itemSelect'] = false
  336. }
  337. orderSelect.selectData.delete(queryParams.pageNo) //移除该页面
  338. } else {
  339. //当前页选中状态中 默认全选中
  340. orderSelect.selectData.set(queryParams.pageNo, new Set<string>())
  341. for (i = 0; i < list.value.length; i++) {
  342. list.value[i]['itemSelect'] = true
  343. orderSelect.selectData.get(queryParams.pageNo)!.add(list.value[i].id)
  344. //选中数量增加
  345. orderSelect.selectTotal += 1
  346. //对于登记过取消状态中的数据排除
  347. unSelectListRecord(list.value[i].id, 'del')
  348. }
  349. }
  350. }
  351. //所有页按钮
  352. if (command === '2') {
  353. orderSelect.selectAllFlag = !orderSelect.selectAllFlag
  354. if (orderSelect.selectAllFlag) {
  355. //打勾勾 //全选
  356. orderSelect.selectData?.set(queryParams.pageNo, new Set<string>())
  357. for (i = 0; i < list.value.length; i++) {
  358. list.value[i]['itemSelect'] = true
  359. orderSelect.selectData?.get(queryParams.pageNo)?.add(list.value[i].id) //id是主键不重复
  360. }
  361. orderSelect.selectTotal = total.value
  362. } else {
  363. //取消勾勾
  364. for (i; i < list.value.length; i++) {
  365. list.value[i]['itemSelect'] = false
  366. }
  367. initSelect() //重置之前选中的类容清空
  368. }
  369. }
  370. }
  371. //对全选状态中的 单选或者当前页面单选时登记取消的数据
  372. const unSelectListRecord = (id: string, op: string) => {
  373. if (!orderSelect.selectAllFlag) {
  374. return
  375. }
  376. if (op == 'add') {
  377. orderSelect.unSelectList.add(id)
  378. } else {
  379. orderSelect.unSelectList.delete(id)
  380. }
  381. }
  382. /***复选框选中 */
  383. const handcheckclick = (row: any) => {
  384. if (row.itemSelect) {
  385. orderSelect.selectTotal += 1
  386. if (!orderSelect.selectData.has(queryParams.pageNo)) {
  387. orderSelect.selectData?.set(queryParams.pageNo, new Set<string>())
  388. }
  389. orderSelect.selectData?.get(queryParams.pageNo)?.add(row.id)
  390. unSelectListRecord(row.id, 'del')
  391. } else {
  392. orderSelect.selectTotal -= 1
  393. orderSelect.selectData.get(queryParams.pageNo)?.delete(row.id)
  394. unSelectListRecord(row.id, 'add')
  395. }
  396. }
  397. /**
  398. * 导出数据
  399. */
  400. const handleExport = async () => {
  401. try {
  402. // 导出的二次确认
  403. await message.exportConfirm()
  404. //增加查询条件 用于全选时后台查询数据
  405. orderSelect.queryParams = queryParams
  406. // 发起导出
  407. exportLoading.value = true
  408. //全选时 根据上送的条件查询所有数据,在排除unseleectList 数据,
  409. //非全选时, 根据上送的selectData 直接查询数据 后台实现导出数据接口即可
  410. console.log(orderSelect)
  411. download.excel(orderSelect as any, '订单信息.xls') //?
  412. } catch {
  413. } finally {
  414. exportLoading.value = false
  415. }
  416. //TODO
  417. exportLoading.value = false
  418. }
  419. /** 搜索按钮操作 */
  420. const handleQuery = () => {
  421. getList()
  422. }
  423. /** 重置按钮操作 */
  424. const resetQuery = () => {
  425. queryFormRef.value.resetFields()
  426. queryType.v = '' //重置
  427. queryType.k = ''
  428. //休眠0.1s 等待watch响应
  429. setTimeout(() => {
  430. initSelect() //重置对选中设置恢复初始状态
  431. handleQuery()
  432. }, 100)
  433. }
  434. /**选中状态初始化**/
  435. const initSelect = () => {
  436. orderSelect.queryParams = {} as TradeOrderPageReqVO
  437. orderSelect.selectTotal = 0
  438. orderSelect.selectAllFlag = false
  439. orderSelect.selectData?.clear()
  440. orderSelect.unSelectList?.clear()
  441. }
  442. const getList = async () => {
  443. loading.value = true
  444. try {
  445. const data = await TradeOrderApi.getOrderList(queryParams)
  446. list.value = data.list
  447. total.value = data.total
  448. let i = 0
  449. if (orderSelect.selectData && orderSelect.selectData.has(queryParams.pageNo)) {
  450. //该页面已经加载过了。直接按照之前状态设置选中状态值
  451. for (i = 0; i < list.value.length; i++) {
  452. if (orderSelect.selectData.get(queryParams.pageNo)!.has(list.value[i].id)) {
  453. list.value[i]['itemSelect'] = true //之前已经选取过了
  454. } else {
  455. list.value[i]['itemSelect'] = false
  456. }
  457. }
  458. } else if (orderSelect.selectAllFlag) {
  459. //全选状态中 首次加载页面 默认全部选中
  460. orderSelect.selectData.set(queryParams.pageNo, new Set<string>())
  461. for (i = 0; i < list.value.length; i++) {
  462. list.value[i]['itemSelect'] = true
  463. orderSelect.selectData.get(queryParams.pageNo)!.add(list.value[i].id)
  464. }
  465. } else {
  466. //非全选状态中 首次加载默认非选中状态
  467. for (i; i < list.value.length; i++) {
  468. list.value[i]['itemSelect'] = false //设置状态为未选中状态
  469. }
  470. }
  471. } finally {
  472. loading.value = false
  473. }
  474. }
  475. /**
  476. * 跳转订单详情
  477. */
  478. const showOrderDetail = (row: any) => {
  479. push({ name: 'TradeOrderDetail', query: { id: row.id } })
  480. }
  481. /**
  482. * 跳转用户详情
  483. */
  484. const goUserDetail = (row: any) => {
  485. console.log('TODO User Detail: ' + row.userId)
  486. }
  487. /**
  488. * 发货
  489. */
  490. const sendXX = (row: any) => {
  491. console.log('TODO Send XX: ' + row.no)
  492. }
  493. /**
  494. * 商品图预览
  495. * @param imgUrl
  496. */
  497. const imagePreview = (imgUrl: string) => {
  498. imageViewerList.value = [imgUrl]
  499. imgViewVisible.value = true
  500. }
  501. //针对订单搜索类型和值进行调整 使用监听器
  502. watch(
  503. () => [queryType.k, queryType.v],
  504. ([newK, newV], [oldK]) => {
  505. //重置oldK对应得value
  506. if (oldK != newK) {
  507. if (oldK == 'no' && queryParams.no != '') {
  508. queryParams.no = ''
  509. } else if (oldK == 'userId' && queryParams.userId != '') {
  510. queryParams.userId = ''
  511. } else if (oldK == 'userNickname' && queryParams.userNickname != '') {
  512. queryParams.userNickname = ''
  513. } else if (oldK == 'userMobile' && queryParams.userMobile !== '') {
  514. queryParams.userMobile = ''
  515. } else if (oldK == 'spuName' && queryParams.spuName !== '') {
  516. queryParams.spuName = ''
  517. } else if (oldK == 'itemCount' && queryParams.itemCount !== '') {
  518. queryParams.itemCount = ''
  519. } else if (oldK == '' && queryParams.all !== '') {
  520. queryParams.all = ''
  521. }
  522. }
  523. // 根据选中得k设置Value
  524. if (newK == 'no') {
  525. queryParams.no = newV
  526. } else if (newK == 'userId') {
  527. queryParams.userId = newV
  528. } else if (newK == 'userNickname') {
  529. queryParams.userNickname = newV
  530. } else if (newK == 'userMobile') {
  531. queryParams.userMobile = newV
  532. } else if (newK == 'spuName') {
  533. queryParams.spuName = newV
  534. } else if (newK == 'itemCount') {
  535. queryParams.itemCount = newV
  536. } else if (newK == '') {
  537. queryParams.all = newV
  538. }
  539. }
  540. )
  541. /** 初始化 **/
  542. onMounted(() => {
  543. initSelect()
  544. getList()
  545. })
  546. </script>