Quellcode durchsuchen

Merge branch 'dev' of https://git.citupro.com/zhengnaiwen_citu/menduner into dev

lifanagju_citu vor 6 Monaten
Ursprung
Commit
99f7c6130c

+ 4 - 0
components.d.ts

@@ -29,6 +29,7 @@ declare module 'vue' {
     CtTextField: typeof import('./src/components/CtVuetify/CtTextField/index.vue')['default']
     DatePicker: typeof import('./src/components/DatePicker/index.vue')['default']
     Echarts: typeof import('./src/components/Echarts/index.vue')['default']
+    ElCascader: typeof import('element-plus/es')['ElCascader']
     ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
     Empty: typeof import('./src/components/Empty/index.vue')['default']
     File: typeof import('./src/components/Upload/file.vue')['default']
@@ -70,4 +71,7 @@ declare module 'vue' {
     VerifySlide: typeof import('./src/components/Verifition/Verify/VerifySlide.vue')['default']
     WangEditor: typeof import('./src/components/FormUI/wangEditor/index.vue')['default']
   }
+  export interface ComponentCustomProperties {
+    vLoading: typeof import('element-plus/es')['ElLoadingDirective']
+  }
 }

+ 34 - 0
src/api/mall/address.js

@@ -0,0 +1,34 @@
+import request from '@/config/axios'
+
+// 获取收货地址列表
+export const getMallUserAddressList = async () => {
+  return request.get({
+    url: '/app-api/member/address/list'
+  })
+}
+
+// 删除收货地址
+export const deleteMallUserAddress = async (id) => {
+  return request.delete({
+    url: '/app-api/member/address/delete',
+    params: {
+      id
+    }
+  })
+}
+
+// 创建收货地址
+export const createMallUserAddress = async (data) => {
+  return request.post({
+    url: '/app-api/member/address/create',
+    data
+  })
+}
+
+// 更新收货地址
+export const updateMallUserAddress = async (data) => {
+  return request.put({
+    url: '/app-api/member/address/update',
+    data
+  })
+}

+ 5 - 1
src/views/mall/components/navbar.vue

@@ -7,7 +7,11 @@
           <v-icon>mdi-account-circle-outline</v-icon>
           我的
         </span>
-        <span class="cursor-pointer">
+        <span class="cursor-pointer" @click="router.push('/pointsExchange')">
+          <v-icon>mdi-octagram-outline</v-icon>
+          积分兑换
+        </span>
+        <span class="cursor-pointer mx-8">
           <v-icon size="20">mdi-cart-outline</v-icon>
           购物车
         </span>

+ 2 - 2
src/views/mall/pointExchange/index.vue

@@ -101,10 +101,9 @@ const formItems = ref({
       default: [],
       label: '收货地址',
       itemText: 'name',
-      itemValue: 'id',
+      itemValue: 'name',
       required: true,
       clearable: false,
-      disabled: true,
       emitPath: true,
       items: []
     },
@@ -162,6 +161,7 @@ const handleSubmit = async () =>{
   if (obj.type) obj.contactAddress = obj.address.join('') + obj.contactAddress
   delete obj.address 
   if (!obj.contactName) obj.contactName = localStorage.getItem('baseInfo') ? JSON.parse(localStorage.getItem('baseInfo')).name : '--'
+
   await redeemSubmit(obj)
   Snackbar.success('提交成功')
   showDetail.value = false

+ 152 - 1
src/views/mall/user/address/index.vue

@@ -1,9 +1,160 @@
 <template>
-  <div>address-index</div>
+  <CtTable
+    class="mt-3"
+    :items="items"
+    :headers="headers"
+    :loading="false"
+    :elevation="0"
+    :isTools="true"
+    :showPage="false"
+    itemKey="id"
+    @add="handleAdd"
+  >
+    <template #actions="{ item }">
+      <v-btn color="primary" @click.stop="handleEdit(item)" variant="text">编辑</v-btn>
+      <v-btn color="error" @click.stop="handleDelete(item)" variant="text">删除</v-btn>
+      <v-btn v-if="!item.defaultStatus" color="success" @click.stop="handleSetDefault(item)" variant="text">设为默认</v-btn>
+    </template>
+  </CtTable>
+
+  <CtDialog :visible="showDialog" titleClass="text-h6" :footer="true" :widthType="3" :title="isAdd ? '新增收货地址' : '编辑收货地址'" @submit="handleSubmit" @close="handleClose">
+    <CtForm ref="CtFormRef" :items="formItems">
+      <template #defaultStatus="{ item }">
+        <div class="d-flex align-center">
+          <span class="color-666 mr-5">设为默认地址</span>
+          <v-switch v-model="item.value" label="" hide-details color="primary" inset></v-switch>
+        </div>
+      </template>
+    </CtForm>
+  </CtDialog>
 </template>
 
 <script setup>
 defineOptions({ name: 'mall-user-address-index' })
+import { ref } from 'vue'
+import { getMallUserAddressList, deleteMallUserAddress, createMallUserAddress, updateMallUserAddress } from '@/api/mall/address'
+import Confirm from '@/plugins/confirm'
+import { getDict } from '@/hooks/web/useDictionaries'
+import Snackbar from '@/plugins/snackbar'
+
+const isAdd = ref(true)
+const showDialog = ref(false)
+const items = ref([])
+const headers = [
+  { title: '收货人', key: 'name', sortable: false },
+  { title: '联系电话', key: 'mobile', sortable: false },
+  { title: '所在地区', key: 'areaName', sortable: false },
+  { title: '详细地址', key: 'detailAddress', sortable: false },
+  { title: '是否默认', key: 'defaultStatus', sortable: false, value: item => item.defaultStatus ? '是' : '否' },
+  { title: '操作', key: 'actions', sortable: false }
+]
+const CtFormRef = ref()
+const formItems = ref({
+  options: [
+    {
+      type: 'text',
+      key: 'name',
+      value: null,
+      default: localStorage.getItem('baseInfo') ? JSON.parse(localStorage.getItem('baseInfo')).name : '',
+      hide: false,
+      label: '收货人姓名 *',
+      rules: [v => !!v || '请输入收货人姓名'],
+    },
+    {
+      type: 'text',
+      key: 'mobile',
+      value: null,
+      default: localStorage.getItem('userInfo') ? JSON.parse(localStorage.getItem('userInfo')).phone : '',
+      label: '收货人联系电话 *',
+      outlined: true,
+      rules: [v => !!v || '请填写收货人联系电话']
+    },
+    {
+      type: 'cascade',
+      key: 'areaId',
+      value: [],
+      default: [],
+      label: '省市区',
+      itemText: 'name',
+      itemValue: 'id',
+      required: true,
+      clearable: false,
+      emitPath: true,
+      items: []
+    },
+    {
+      type: 'textarea',
+      key: 'detailAddress',
+      value: '',
+      default: null,
+      label: '详细地址 *',
+      rules: [ v => !!v || '请填写详细地址' ]
+    },
+    {
+      slotName: 'defaultStatus',
+      key: 'defaultStatus',
+      value: false
+    }
+  ]
+})
+getDict('areaTreeData', null, 'areaTreeData').then(({ data }) => {
+  data = data?.length && data || []
+  formItems.value.options.find(e => e.key === 'areaId').items = data
+})
+
+// 获取地址列表
+const getAddressList = async () => {
+  const data = await getMallUserAddressList()
+  items.value = data
+}
+getAddressList()
+
+// 新增
+const handleAdd = () => {
+  showDialog.value = true
+  isAdd.value = true
+}
+
+// 设置为默认地址
+const handleSetDefault = async (item) => {
+  Confirm('系统提示', '是否确定设置该地址为默认收货地址?').then(async () => {
+    await updateMallUserAddress({ ...item, defaultStatus: true })
+    Snackbar.success('设置成功')
+    await getAddressList()
+  })
+}
+
+// 编辑
+const handleEdit = (item) => {
+  isAdd.value = false
+  formItems.value.options.forEach(e => e.value = item[e.key])
+  showDialog.value = true
+}
+
+const handleSubmit = async () => {
+  const { valid } = await CtFormRef.value.formRef.validate()
+  if (!valid) return
+  const obj = {}
+  formItems.value.options.forEach(e => obj[e.key] = e.value)
+  console.log(obj, 'submit')
+  // try {
+  //   isAdd.value ? await createMallUserAddress(obj) : await updateMallUserAddress(obj)
+  // } catch {}
+}
+
+const handleClose = () => {
+  showDialog.value = false
+
+}
+
+// 删除
+const handleDelete = (item) => {
+  Confirm('系统提示', '是否确认删除该收货地址?').then(async () => {
+    await deleteMallUserAddress(item.id)
+    Snackbar.success('删除成功')
+    await getAddressList()
+  })
+}
 </script>
 
 <style scoped lang="scss">

+ 74 - 11
src/views/mall/user/order/detail.vue

@@ -1,10 +1,64 @@
 <template>
   <div>
     <Navbar class="mb-3" />
-    <v-card class="default-width card-box mb-5 pa-5">
-      <h3 class="mb-3">订单详情</h3>
-      <v-divider></v-divider>
-    </v-card>
+    <div class="default-width pb-5">
+      <v-banner color="primary" :text="formatOrderStatusDescription(order)" style="background-color: #00897B; color: #fff;"></v-banner>
+
+      <v-row no-gutters class="mt-3">
+        <v-col span="6">
+          <!-- 订单信息 -->
+          <v-card class="pa-5">
+            <h3>订单信息</h3>
+            <v-divider class="my-3"></v-divider>
+            <div class="font-size-15 color-666">
+              <p>订单编号:{{ order.no }}</p>
+              <p class="my-3">下单时间:{{ timesTampChange(order.createTime) }}</p>
+              <p>支付时间:{{ timesTampChange(order.payTime) }}</p>
+              <p class="mt-3">支付方式:{{ order.payChannelName }}</p>
+            </div>
+          </v-card>
+        </v-col>
+        <v-col span="6" class="ml-3">
+          <!-- 物流信息 -->
+          <v-card class="pa-5" style="height: 100%">
+            <h3>物流信息</h3>
+            <v-divider class="my-3"></v-divider>
+            <div class="font-size-15 color-666">
+              <p>收货地址:{{ order.receiverName }},{{ order.receiverMobile }},{{ order.receiverAreaName }} {{ order.receiverDetailAddress }}</p>
+              <p class="my-3">物流公司:{{ order.logisticsName }}</p>
+              <p>运单号:{{ order.logisticsNo }}</p>
+            </div>
+          </v-card>
+        </v-col>
+      </v-row>
+
+      <!-- 商品列表 -->
+      <v-card class="my-3 pa-5">
+        <h3 class="mb-3">商品列表</h3>
+        <v-divider></v-divider>
+
+        <CtTable
+          class="mt-3"
+          :items="order.items"
+          :headers="headers"
+          :loading="false"
+          :elevation="0"
+          :isTools="false"
+          :showPage="false"
+          itemKey="id"
+        >
+          <template #picUrl="{ item }">
+            <v-img :src="item.picUrl" width="90" height="90"></v-img>
+          </template>
+          <template #spuName="{ item }">
+            <span class="color-primary cursor-pointer" @click="handleToGoodsDetail(item)">{{ item.spuName }}</span>
+          </template>
+        </CtTable>
+        <div class="text-end color-primary mr-3 mt-5 font-size-20">
+          共{{ order.productCount }}件商品,合计:¥{{ fen2yuan(order.payPrice) }}
+        </div>
+      </v-card>
+    </div>
   </div>
 </template>
 
@@ -15,22 +69,31 @@ import { useRouter } from 'vue-router'
 import { getMallOrderDetail } from '@/api/mall/user'
 import Snackbar from '@/plugins/snackbar'
 import Navbar from '../../components/navbar.vue'
+import { timesTampChange } from '@/utils/date'
+import { fen2yuan, formatOrderStatusDescription } from '@/hooks/web/useGoods'
 
 const router = useRouter()
 const { id } = router.currentRoute.value.params
 const order = ref({})
 
+const headers = [
+  { title: '', key: 'picUrl', sortable: false },
+  { title: '商品名称', key: 'spuName', sortable: false },
+  { title: '规格', key: 'contactAddress', sortable: false, value: item => item.properties.map((property) => property.valueName).join(' ') },
+  { title: '单价', key: 'price', sortable: false, value: item => '¥' + fen2yuan(item.price) },
+  { title: '数量', key: 'count', sortable: false }
+]
+
 onMounted(async () =>{
-  if (!id) {
-    Snackbar.warning('参数错误')
-    setTimeout(() => {
-      router.go(-1)
-    }, 1000)
-    return
-  }
+  if (!id) return Snackbar.error('订单不存在')
   const data = await getMallOrderDetail(id)
   order.value = data
 })
+
+// 跳转商品详情
+const handleToGoodsDetail = (item) => {
+  window.open(`/mall/goodsDetail/${item.spuId}`)
+}
 </script>
 
 <style scoped lang="scss">

+ 1 - 1
src/views/mall/user/order/index.vue

@@ -38,7 +38,7 @@
           <v-btn v-if="val.buttons.includes('express')" class="mt-2" variant="tonal" rounded="xl">查看物流</v-btn>
           <v-btn v-if="val.buttons.includes('cancel')" class="mt-2" variant="tonal" rounded="xl">取消订单</v-btn>
           <v-btn v-if="val.buttons.includes('delete')" class="mt-2" variant="tonal" color="error" rounded="xl">删除订单</v-btn>
-          <v-btn v-if="val.buttons.includes('pay')" class="mt-2" variant="tonal" rounded="xl">继续支付</v-btn>
+          <v-btn v-if="val.buttons.includes('pay')" class="mt-2 ml-3" variant="tonal" rounded="xl">继续支付</v-btn>
         </div>
       </div>
       <CtPagination :total="total" :page="queryParams.pageNo" :limit="queryParams.pageSize" @handleChange="handleChangePage"></CtPagination>