Browse Source

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

zhengnaiwen_citu 4 months ago
parent
commit
87a442cb3f
46 changed files with 695 additions and 377 deletions
  1. 1 0
      components.d.ts
  2. 58 0
      package-lock.json
  3. 1 0
      package.json
  4. 40 0
      src/api/mall/cart.js
  5. 8 2
      src/components/CtTable/index.vue
  6. 2 1
      src/components/Enterprise/hotPromoted.vue
  7. 2 1
      src/components/Enterprise/info.vue
  8. 5 2
      src/components/FormUI/TextInput/index.vue
  9. 10 80
      src/components/FormUI/su-number-box/su-number-box.vue
  10. 2 1
      src/components/Position/item.vue
  11. 2 1
      src/components/Position/longCompany.vue
  12. 2 1
      src/components/Position/longStrip.vue
  13. 3 1
      src/components/PositionLongStrip/item.vue
  14. 2 1
      src/layout/company/navBar.vue
  15. 3 0
      src/main.js
  16. 8 1
      src/permission.js
  17. 3 1
      src/plugins/vuetify.js
  18. 9 1
      src/router/modules/recruit.js
  19. 5 0
      src/utils/getText.js
  20. 1 1
      src/version.js
  21. 217 0
      src/views/mall/cart/index.vue
  22. 6 2
      src/views/mall/components/GoodsItem/index.vue
  23. 18 43
      src/views/mall/components/details.vue
  24. 35 34
      src/views/mall/components/details/comment-item.vue
  25. 9 0
      src/views/mall/components/details/describe.vue
  26. 2 43
      src/views/mall/components/details/detail-comment-card.vue
  27. 3 8
      src/views/mall/components/details/order/addressSelection.vue
  28. 11 115
      src/views/mall/components/details/order/confirm.vue
  29. 14 13
      src/views/mall/components/details/s-select-sku.vue
  30. 1 1
      src/views/mall/components/navbar.vue
  31. 71 0
      src/views/mall/components/prizeDraw.vue
  32. 5 6
      src/views/mall/components/s-goods-item/index.vue
  33. 0 1
      src/views/mall/user/address/index.vue
  34. 2 1
      src/views/publicRecruitment/components/table.vue
  35. 2 1
      src/views/recruit/components/message/components/chatting.vue
  36. 2 1
      src/views/recruit/components/message/index.vue
  37. 4 2
      src/views/recruit/enterprise/systemManagement/groupAccount/components/record.vue
  38. 4 4
      src/views/recruit/enterprise/systemManagement/groupAccount/index.vue
  39. 2 1
      src/views/recruit/personal/PersonalCenter/jobFeedback/components/interview/item.vue
  40. 2 1
      src/views/recruit/personal/PersonalCenter/jobFeedback/components/seenMe.vue
  41. 3 1
      src/views/recruit/personal/company/components/companyItem.vue
  42. 2 1
      src/views/recruit/personal/companyDetail/index.vue
  43. 1 1
      src/views/recruit/personal/home/components/advertisement/dynamic/intercontinental.vue
  44. 2 1
      src/views/recruit/personal/position/components/details.vue
  45. 2 1
      src/views/recruit/personal/recommend/components/positionList.vue
  46. 108 0
      vite.config.mjs.timestamp-1735030220221-ec7db8c3a2187.mjs

+ 1 - 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']

+ 58 - 0
package-lock.json

@@ -8,6 +8,7 @@
       "name": "vuetify-project",
       "version": "0.0.0",
       "dependencies": {
+        "@lucky-canvas/vue": "^0.1.11",
         "@mdi/font": "7.0.96",
         "@vuepic/vue-datepicker": "^8.7.0",
         "@wangeditor/editor": "^5.1.23",
@@ -696,6 +697,57 @@
         "@jridgewell/sourcemap-codec": "^1.4.14"
       }
     },
+    "node_modules/@lucky-canvas/vue": {
+      "version": "0.1.11",
+      "resolved": "https://registry.npmmirror.com/@lucky-canvas/vue/-/vue-0.1.11.tgz",
+      "integrity": "sha512-5vm0txSKRBtMgrE/HZEvw1joSTx9NTdAkc8tBp/aX0LxyhQtiTVBLsRgdYUK/OiURCL8bo+046BTGnV+Q4JFlg==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@vue/composition-api": "^1.0.0",
+        "lucky-canvas": "^1.7.23",
+        "vue-demi": "^0.7.4"
+      },
+      "peerDependencies": {
+        "vue": "^2.0.0 || >=3.0.0-rc.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@lucky-canvas/vue/node_modules/@vue/composition-api": {
+      "version": "1.7.2",
+      "resolved": "https://registry.npmmirror.com/@vue/composition-api/-/composition-api-1.7.2.tgz",
+      "integrity": "sha512-M8jm9J/laYrYT02665HkZ5l2fWTK4dcVg3BsDHm/pfz+MjDYwX+9FUaZyGwEyXEDonQYRCo0H7aLgdklcIELjw==",
+      "license": "MIT",
+      "peerDependencies": {
+        "vue": ">= 2.5 < 2.7"
+      }
+    },
+    "node_modules/@lucky-canvas/vue/node_modules/vue-demi": {
+      "version": "0.7.5",
+      "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.7.5.tgz",
+      "integrity": "sha512-eFSQSvbQdY7C9ujOzvM6tn7XxwLjn0VQDXQsiYBLBwf28Na+2nTQR4BBBcomhmdP6mmHlBKAwarq6a0BPG87hQ==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "vue-demi-fix": "bin/vue-demi-fix.js",
+        "vue-demi-switch": "bin/vue-demi-switch.js"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.0.0-beta.1",
+        "vue": "^2.6.0 || >=3.0.0-rc.1"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/@mdi/font": {
       "version": "7.0.96",
       "resolved": "https://registry.npmmirror.com/@mdi/font/-/font-7.0.96.tgz",
@@ -5004,6 +5056,12 @@
         "node": ">=10"
       }
     },
+    "node_modules/lucky-canvas": {
+      "version": "1.7.27",
+      "resolved": "https://registry.npmmirror.com/lucky-canvas/-/lucky-canvas-1.7.27.tgz",
+      "integrity": "sha512-Ftz6qD+863bI7xijBmZg3dw3cNEc7odPr70EZQcGA14y3TgTAzH65HPosOCd6kKUlMwhntBaHMx3onoj9MtJRQ==",
+      "license": "Apache-2.0"
+    },
     "node_modules/magic-string": {
       "version": "0.30.11",
       "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.11.tgz",

+ 1 - 0
package.json

@@ -10,6 +10,7 @@
     "format": "prettier --write src/"
   },
   "dependencies": {
+    "@lucky-canvas/vue": "^0.1.11",
     "@mdi/font": "7.0.96",
     "@vuepic/vue-datepicker": "^8.7.0",
     "@wangeditor/editor": "^5.1.23",

+ 40 - 0
src/api/mall/cart.js

@@ -0,0 +1,40 @@
+import request from '@/config/axios'
+
+// 获取购物车列表
+export const getMallUserCartList = async () => {
+  return request.get({
+    url: '/app-api/trade/cart/list'
+  })
+}
+
+// 从购物车中删除商品
+export const deleteCartGoods = async (ids) => {
+  return request.delete({
+    url: '/app-api/trade/cart/delete',
+    params: { ids }
+  })
+}
+
+// 更新购物车商品选中状态
+export const updateCartSelected = async (data) => {
+  return request.put({
+    url: '/app-api/trade/cart/update-selected',
+    data
+  })
+}
+
+// 更新购物车商品数量
+export const updateCartCount = async (data) => {
+  return request.put({
+    url: '/app-api/trade/cart/update-count',
+    data
+  })
+}
+
+// 添加商品到购物车
+export const addCart = async (data) => {
+  return request.post({
+    url: '/app-api/trade/cart/add',
+    data
+  })
+}

+ 8 - 2
src/components/CtTable/index.vue

@@ -9,6 +9,7 @@
     </div>
     <v-data-table
       ref="table"
+      v-model="selected"
       :class="`elevation-${elevation} tableColor ${noRadius ? 'noRadius' : ''}`"
       :headers="headers"
       :items="items"
@@ -53,10 +54,12 @@
 
 <script setup>
 defineOptions({ name: 'CtTable'})
-import { ref, computed, useSlots } from 'vue'
+import { ref, computed, useSlots, watch } from 'vue'
 
+const selected = ref([])
 const emit = defineEmits(['pageHandleChange', 'del', 'edit', 'add', 'selected'])
-defineProps({
+const props = defineProps({
+  modelValue: Array,
   elevation: {
     type: [Number, String],
     default: 0
@@ -133,6 +136,9 @@ defineProps({
     default: 'single'
   }
 })
+watch(() => props.modelValue, (val) => {
+  selected.value = val
+}, { deep: true, immediate: true })
 
 const table = ref()
 const slot = useSlots()

+ 2 - 1
src/components/Enterprise/hotPromoted.vue

@@ -8,7 +8,7 @@
             <v-img :src="item?.enterprise.logoUrl || 'https://minio.citupro.com/dev/menduner/company-avatar.png'" alt="" width="77" height="77" style="border-radius: 4px;"/>
           </div>
           <div class="company-info cursor-pointer">
-            <h3>{{ item.enterprise.anotherName || item.enterprise.name }}</h3>
+            <h3>{{ dealEnterpriseName(item.enterprise.anotherName || item.enterprise.name) }}</h3>
             <p>
               {{ item?.enterprise.scaleName }}
               <span class="septal-line" v-if="item.enterprise.industryName"></span>
@@ -50,6 +50,7 @@
 <script setup name="hotPromoted">
 import { ref, watch } from 'vue'
 import { timesTampChange } from '@/utils/date'
+import { dealEnterpriseName } from '@/utils/getText'
 
 const props = defineProps({
   items: {

+ 2 - 1
src/components/Enterprise/info.vue

@@ -4,7 +4,7 @@
     <div style="height: 50px; line-height: 50px;">
       <v-img class="float-left" :src="props.info.enterprise.logoUrl || 'https://minio.citupro.com/dev/menduner/company-avatar.png'" :width="45" height="45"></v-img>
       <div class="ml-3 float-left">
-        <p class="enterprise-name cursor-pointer" @click="handleEnterprise(0)">{{ props.info.enterprise.anotherName }}</p>
+        <p class="enterprise-name cursor-pointer" @click="handleEnterprise(0)">{{ dealEnterpriseName(props.info.enterprise.anotherName || props.info.enterprise.name) }}</p>
         <!-- <v-icon :color="statusInfo.color" size="20">{{ statusInfo.mdi }}</v-icon>
         <span :style="{'color': statusInfo.color,'font-size': '14px'}">{{ statusInfo.label }}</span> -->
       </div>
@@ -25,6 +25,7 @@
 defineOptions({ name: 'enterprise-info' })
 import { ref } from 'vue'
 import { dealDictObjData } from '@/utils/position'
+import { dealEnterpriseName } from '@/utils/getText'
 // import { getEnterpriseAuthDetails } from '@/api/position'
 
 const props = defineProps({

+ 5 - 2
src/components/FormUI/TextInput/index.vue

@@ -46,7 +46,10 @@ const props = defineProps({item: Object, modelValue: [String, Number]})
 const emit = defineEmits(['update:modelValue', 'change', 'appendClick', 'appendInnerClick', 'enter', 'blur', 'focus'])
 const item = props.item
 const inputType = ref(item.type === 'phoneNumber' ? 'number' : item.type)
-const value = ref(props.modelValue)
+
+const value = ref('')
+if (props.modelValue) value.value = props.modelValue.replace(/&amp;/g, '&')
+
 const searchDebouncedTime = item?.searchDebouncedTime === 0 ? ref(0) : ref(500)
 if (inputType.value === 'number' && item.integer) searchDebouncedTime.value = 0
 
@@ -62,7 +65,7 @@ const valueDeal = (val) => {
 
 watch(() => props.modelValue, (newVal) => {
   const dealData = valueDeal(newVal)
-  value.value = dealData
+  value.value = dealData ? dealData.replace(/&amp;/g, '&') : ''
 })
 
 const modelValueUpDate = (val) => {

+ 10 - 80
src/components/FormUI/su-number-box/su-number-box.vue

@@ -1,26 +1,11 @@
 <template>
   <view class="uni-numbox">
-    <!-- <view @click="_calcValue('minus')" class="uni-numbox__minus uni-numbox-btns" :style="{ background }"> -->
-    <!-- <text class="uni-numbox--text" :class="{ 'uni-numbox--disabled': inputValue <= min || disabled }"
-				:style="{ color }">
-				-
-			</text> -->
-    <!-- <text
-      class="cicon-move-round"
-      :class="{
-        'uni-numbox--disabled': inputValue <= min || disabled,
-        'groupon-btn': activity === 'groupon',
-        'seckill-btn': activity === 'seckill',
-      }"
-      @click="_calcValue('minus')"
-    ></text> -->
     <v-btn
       icon="mdi-minus" 
       size="x-small" 
       :disabled="inputValue <= min || disabled" 
       @click="_calcValue('minus')"
     ></v-btn>
-    <!-- </view> -->
     <input
       :disabled="disabled"
       @focus="_onFocus"
@@ -30,18 +15,6 @@
       v-model="inputValue"
       :style="{ color }"
     />
-    <!-- <view @click="_calcValue('plus')" class="uni-numbox__plus uni-numbox-btns">
-			<text class="uni-numbox--text" :class="{ 'uni-numbox--disabled': inputValue >= max || disabled }">+</text>
-		</view> -->
-    <!-- <text
-      class="cicon-add-round"
-      :class="{
-        'uni-numbox--disabled': inputValue >= max || disabled,
-        'groupon-btn': activity === 'groupon',
-        'seckill-btn': activity === 'seckill',
-      }"
-      @click="_calcValue('plus')"
-    ></text> -->
     <v-btn
       icon="mdi-plus" 
       size="x-small" 
@@ -182,11 +155,6 @@
         return scale;
       },
       _onBlur(event) {
-        // let value = event.detail.value;
-        // if (!value) {
-        //   // this.inputValue = 0;
-        //   return;
-        // }
         let value = this.inputValue;
         this.$emit('blur', value);
         value = +value;
@@ -207,52 +175,14 @@
   };
 </script>
 <style lang="scss" scoped>
-  .uni-numbox .uni-numbox--disabled {
-    color: #c0c0c0 !important;
-    /* #ifdef H5 */
-    cursor: not-allowed;
-    /* #endif */
-  }
-
-  .uni-numbox {
-    /* #ifndef APP-NVUE */
-    display: flex;
-    /* #endif */
-    align-items: center;
-  }
-
-  .uni-numbox__value {
-    width: 70px;
-    text-align: center;
-    // font-size: 30rpx;
-  }
-
-  .cicon-move-round {
-    font-size: 44rpx;
-    color: var(--ui-BG-Main);
-  }
-
-  .cicon-add-round {
-    font-size: 44rpx;
-    color: var(--ui-BG-Main);
-  }
-
-  .groupon-btn {
-    color: #ff6000;
-  }
-
-  .seckill-btn {
-    color: #ff5854;
-  }
-
-  input::-webkit-outer-spin-button, input::-webkit-inner-spin-button{
-    -webkit-appearance: none !important;
-    margin: 0; 
-  }
-  .inputItem {
-    width: 70px; 
-    border: 1px solid #eee; 
-    padding: 2px 5px; 
-    text-align: center;
-  }
+input::-webkit-outer-spin-button, input::-webkit-inner-spin-button{
+  -webkit-appearance: none !important;
+  margin: 0; 
+}
+.inputItem {
+  width: 70px; 
+  border: 1px solid #eee; 
+  padding: 2px 5px; 
+  text-align: center;
+}
 </style>

+ 2 - 1
src/components/Position/item.vue

@@ -37,7 +37,7 @@
                 <v-img :src="item.logoUrl || 'https://minio.citupro.com/dev/menduner/company-avatar.png'" />
               </v-avatar>
               <span class="names ml-2 font-size-14 ellipsis" style="max-width: 88%;">
-                {{ item.anotherName }}
+                {{ dealEnterpriseName(item.anotherName || item.enterpriseName) }}
                 <span class="color-999 font-size-13 ml-3">
                   <span>{{ item.industryName }}</span>
                   <span class="septal-line" v-if="item.industryName && item.scaleName"></span>
@@ -57,6 +57,7 @@ defineOptions({ name: 'position-card-item' })
 import { ref, watch } from 'vue'
 import { commissionCalculation } from '@/utils/position'
 import { timesTampChange } from '@/utils/date'
+import { dealEnterpriseName } from '@/utils/getText';
 
 const props = defineProps({
   items: {

+ 2 - 1
src/components/Position/longCompany.vue

@@ -6,7 +6,7 @@
           <div class="float-left mr-5">
             <v-img :src="item.logoUrl || 'https://minio.citupro.com/dev/menduner/company-avatar.png'" :alt="item.anotherName" :width="40" style="height: 40px;border-radius: 4px;"/>
           </div>
-          <h3 :class="{'default-active': item.active }" class="cursor-pointer" @click="handleClickEnterprise(item, 'briefIntroduction')">{{ item.anotherName }}</h3>
+          <h3 :class="{'default-active': item.active }" class="cursor-pointer" @click="handleClickEnterprise(item, 'briefIntroduction')">{{ dealEnterpriseName(item.anotherName || item.name) }}</h3>
           <p>{{ item.industryName }}<span v-if="item.industryName && item.scaleName" class="mx-2">|</span>{{ item.scaleName }}</p>
         </div>
         <div v-if="item.active">
@@ -33,6 +33,7 @@
 defineOptions({ name: 'long-company-card'})
 import Snackbar from '@/plugins/snackbar'
 import { useI18n } from '@/hooks/web/useI18n'
+import { dealEnterpriseName } from '@/utils/getText'
 import { getEnterpriseUnsubscribe } from '@/api/enterprise'
 
 const emits = defineEmits(['refresh'])

+ 2 - 1
src/components/Position/longStrip.vue

@@ -35,7 +35,7 @@
             <v-img width="50" height="50" :src="val.enterprise.logoUrl || 'https://minio.citupro.com/dev/menduner/7.png'"></v-img>
           </div>
           <div class="ml-3">
-            <div class="cursor-pointer info-name" @click="handleToEnterprise(val)">{{ val.enterprise.anotherName }}</div>
+            <div class="cursor-pointer info-name" @click="handleToEnterprise(val)">{{ dealEnterpriseName(val.enterprise.anotherName || val.enterprise.name) }}</div>
             <div class="mt-3 ellipsis color-666 font-size-13" style="max-width: 260px;">
               <span v-for="(k, i) in desc" :key="k">
                 {{ val.enterprise[k] }}
@@ -65,6 +65,7 @@ import loginPage from '@/views/common/loginDialog.vue'
 import { getToken } from '@/utils/auth'
 import { checkPersonBaseInfo } from '@/utils/check'
 import dialogExtend from '@/plugins/dialogExtend'
+import { dealEnterpriseName } from '@/utils/getText'
 
 const emits = defineEmits(['refresh'])
 const { t } = useI18n()

+ 3 - 1
src/components/PositionLongStrip/item.vue

@@ -38,7 +38,7 @@
         <div class="company-info">
           <v-hover>
             <template v-slot:default="{ isHovering, props }">
-              <h3 v-bind="props" :class="{'default-active': isHovering }" class="title1">{{ item.enterprise.anotherName }}</h3>
+              <h3 v-bind="props" :class="{'default-active': isHovering }" class="title1">{{ dealEnterpriseName(item.enterprise.anotherName, item.enterprise.name) }}</h3>
             </template>
           </v-hover>
           <p class="mt-2">
@@ -74,6 +74,8 @@ import { commissionCalculation } from '@/utils/position'
 defineOptions({ name: 'long-strip-position-card-item' })
 import { ref, watch } from 'vue'
 import { getUserAvatar } from '@/utils/avatar'
+import { dealEnterpriseName } from '@/utils/getText'
+
 const props = defineProps({
   items: {
     type: Object,

+ 2 - 1
src/layout/company/navBar.vue

@@ -15,7 +15,7 @@
         <div class="d-flex user-nav align-center">
           <div class="d-flex align-center cursor-pointer">
             <v-img @click="enterpriseClick(2)" rounded width="40" height="40" :src="baseInfo?.logoUrl || 'https://minio.citupro.com/dev/menduner/company-avatar.png'" ></v-img>
-            <span @click="enterpriseClick(1)" class="ml-3 commonHover">{{ baseInfo?.enterpriseAnotherName || baseInfo?.enterpriseName || '--' }}</span>
+            <span @click="enterpriseClick(1)" class="ml-3 commonHover">{{ dealEnterpriseName(baseInfo?.enterpriseAnotherName || baseInfo?.enterpriseName) }}</span>
           </div>
           <div class="ml-3 cursor-pointer border-left border-right px-3 commonHover" @click="handleLogout(false)">我要求职</div>
           <div class="d-flex align-center px-3 border-right cursor-pointer commonHover" @click="router.push('/recruit/enterprise/membershipPackage?key=1')">
@@ -92,6 +92,7 @@ import { useRouter } from 'vue-router'; const router = useRouter()
 import { useI18n } from '@/hooks/web/useI18n'; const { t } = useI18n()
 import MessageNotification from '../message.vue'
 import { getUserAvatar } from '@/utils/avatar'
+import { dealEnterpriseName } from '@/utils/getText';
 defineOptions({ name: 'personal-navbar' })
 
 defineProps({

+ 3 - 0
src/main.js

@@ -27,6 +27,8 @@ import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' // pinia 持
 import VueDatePicker from '@vuepic/vue-datepicker';
 import '@vuepic/vue-datepicker/dist/main.css'
 
+import VueLuckyCanvas from '@lucky-canvas/vue'
+
 import router from './router'
 
 import './permission'
@@ -42,6 +44,7 @@ app.use(ElementPlus)
 app.use(pinia)
 app.use(router)
 app.use(Clipboard)
+app.use(VueLuckyCanvas)
 
 // app.config.globalProperties.$echarts = echarts
 registerPlugins(app)

+ 8 - 1
src/permission.js

@@ -2,10 +2,11 @@ import router from './router'
 import { useNProgress } from '@/hooks/web/useNProgress'
 import { useTitle } from '@/hooks/web/useTitle'
 import { getToken, getIsEnterprise } from '@/utils/auth'
-import { useDictStore } from '@/store/dict'
+// import { useDictStore } from '@/store/dict'
 import { useUserStore } from '@/store/user'
 import Confirm from '@/plugins/confirm'
 import dialogExtend from '@/plugins/dialogExtend'
+import { useMallStore } from '@/store/mall'
 
 const { start, done } = useNProgress()
 // loginType:1.enterprise: 企业路由
@@ -14,6 +15,12 @@ const { start, done } = useNProgress()
 //            3.personalCommon: 无需登录也能访问的页面
 // 路由守卫
 router.beforeEach(async (to, from, next) => {
+  // 获取商城装修模版
+  const mallStore = useMallStore()
+  if (!localStorage.getItem('mallTemplate')) {
+    await mallStore.getMallDiyTemplate()
+  }
+  
   localStorage.setItem('routerTest', to.path) // 本地环境保存代码热更新会导致路径缺失问题
   const tokenIndex = getIsEnterprise() ? 1: 2
   start()

+ 3 - 1
src/plugins/vuetify.js

@@ -11,6 +11,7 @@ import 'vuetify/styles'
 import * as directives from 'vuetify/directives'
 import { VDateInput } from 'vuetify/labs/VDateInput'
 import { VTreeview } from 'vuetify/labs/VTreeview'
+import { VNumberInput } from 'vuetify/labs/VNumberInput'
 
 // Composables
 import { createVuetify } from 'vuetify'
@@ -32,7 +33,8 @@ const myCustomLightTheme = {
 export default createVuetify({
   components: {
     VDateInput,
-    VTreeview
+    VTreeview,
+    VNumberInput
   },
   directives,
   theme: {

+ 9 - 1
src/router/modules/recruit.js

@@ -97,7 +97,15 @@ const recruit = [
         meta: {
           title: '订单详情'
         }
-      }
+      },
+      {
+        path: '/mall/cart',
+        component: () => import('@/views/mall/cart/index.vue'),
+        name: 'mallCart',
+        meta: {
+          title: '我的购物车'
+        }
+      },
     ]
   },
   {

+ 5 - 0
src/utils/getText.js

@@ -52,3 +52,8 @@ export const dealCanBeInputtedSave = (formItem, params) => {
   }
   else { params[formItem.key] = formItem.value; params[formItem.itemTextName] = formItem[formItem.itemTextName] }
 }
+
+// 企业名称处理
+export const dealEnterpriseName = (text) => {
+  return text ? text.replace(/&amp;/g, '&') : ''
+}

+ 1 - 1
src/version.js

@@ -1,2 +1,2 @@
 // 版本号
-export const vue_version = 'v24.12.24.1221'
+export const vue_version = 'v24.12.24.1544'

+ 217 - 0
src/views/mall/cart/index.vue

@@ -0,0 +1,217 @@
+<template>
+  <Navbar class="mb-3" />
+  <v-card class="default-width card-box mb-5 pa-5 resume-box">
+    <div class="resume-header">
+      <div class="resume-title">我的购物车</div>
+      <v-btn color="primary" size="small" variant="text" @click="getCartList"><v-icon>mdi-refresh</v-icon>刷新购物车</v-btn>
+    </div>
+    <div v-if="cartList.length" class="mt-3">
+      <v-data-table
+        v-model="selectedData"
+        :items="cartList"
+        :headers="headers"
+        :loading="false"
+        :elevation="0"
+        :show-select="true"
+        select-strategy="all"
+      >
+        <template v-slot:[`header.data-table-select`]>
+          <v-checkbox v-model="selectAll" hide-details color="primary" @update:modelValue="handleSelectAll"></v-checkbox>
+        </template>
+        <template v-slot:[`item.data-table-select`]="{ item }">
+          <v-checkbox v-model="item.selected" hide-details color="primary" @update:modelValue="val => handleSelect(val, item.id)"></v-checkbox>
+        </template>
+        <template v-slot:[`item.spuName`]="{ item }">
+          <GoodsItem :item="{ ...item.sku, ...item.spu, spuName: item.spu.name, picUrl: item.sku?.picUrl || item.spu?.picUrl }" :showLine="false" :showPriceCount="false" class="mb-1" />
+        </template>
+        <template v-slot:[`item.count`]="{ item }">
+          <v-number-input 
+            v-model="item.count" 
+            color="primary" 
+            :min="1" :max="item.sku.stock"
+            hide-details 
+            control-variant="split" 
+            inset density="compact" 
+            style="width: 170px;"
+            @update:modelValue="val => handleChangeCount(val, item.id)"
+          />
+        </template>
+        <template v-slot:[`item.actions`]="{ item }">
+          <v-btn color="error" @click.stop="handleDelete(false, item.id)" variant="text">删除</v-btn>
+        </template>
+        <template #bottom></template>
+      </v-data-table>
+      <v-divider></v-divider>
+      <div class="d-flex align-center justify-space-between py-4">
+        <div class="d-flex align-center">
+          <!-- <v-checkbox v-model="selectAll" hide-details color="primary" @update:modelValue="handleSelectAll"></v-checkbox>
+          <span class="mr-5" style="color: #777;">全选</span> -->
+          <v-btn :disabled="!selectedData.length" color="error" variant="outlined" @click.stop="handleDelete(true)">删除选中的商品</v-btn>
+        </div>
+        <div class="d-flex align-center">
+          <div class="color-666 mr-8">共{{ totalCount }}件商品,合计:¥{{ fen2yuan(totalPrice) }}</div>
+          <v-btn :disabled="!totalCount" color="primary" @click.stop="handleSettlement">结算<span v-if="totalCount > 0">({{ totalCount }})</span></v-btn>
+        </div>
+      </div>
+    </div>
+    <div v-else class="text-center">
+      <Empty :elevation="false" message="购物车空空如也,去首页逛逛吧" />
+      <v-btn elevation="5" color="primary" @click.stop="router.push('/mall')" size="x-large" width="200">去逛逛</v-btn>
+    </div>
+  </v-card>
+  <!-- 结算 -->
+  <CtDialog :visible="showPay" titleClass="text-h6" :widthType="3" title="订单信息" @submit="handleSubmit" @close="handleClose">
+    <confirm ref="confirmRef" :data="skuInfo" @paySuccess="paySuccess"></confirm>
+  </CtDialog>
+</template>
+
+<script setup>
+defineOptions({ name: 'mall-cart'})
+import { ref, computed, onMounted } from 'vue'
+import Navbar from '../components/navbar.vue'
+import { useRouter } from 'vue-router'
+import { getMallUserCartList } from '@/api/mall/cart'
+import { fen2yuan } from '@/hooks/web/useGoods'
+import GoodsItem from '../components/GoodsItem'
+import { updateCartSelected, updateCartCount, deleteCartGoods } from '@/api/mall/cart'
+import Snackbar from '@/plugins/snackbar'
+import confirm from '@/views/mall/components/details/order/confirm.vue'
+import Confirm from '@/plugins/confirm'
+import { useI18n } from '@/hooks/web/useI18n'; const { t } = useI18n()
+
+const router = useRouter()
+const cartList = ref([])
+const selectAll = ref(false)
+const headers = [
+  { title: '商品信息', key: 'spuName', sortable: false },
+  { title: '单价', key: 'sku.price', sortable: false, value: item => '¥' + fen2yuan(item.sku.price) },
+  { title: '数量', key: 'count', sortable: false },
+  { title: '合计', key: 'totalCount', sortable: false, value: item => '¥' + fen2yuan(item.sku.price * item.count) },
+  { title: '操作', key: 'actions', align: 'center', sortable: false }
+]
+
+// 获取购物车列表
+const getCartList = async () => {
+  const data = await getMallUserCartList()
+  cartList.value = data.validList || []
+  selectAll.value = cartList.value.every(e => e.selected)
+}
+onMounted(async () => {
+  getCartList()
+})
+
+// 全选
+const handleSelectAll = async (selected) => {
+  const ids = cartList.value.map(item => item.id)
+  await updateCartSelected({ ids, selected })
+  getCartList()
+}
+
+// 单选
+const handleSelect = async (selected, id) => {
+  await updateCartSelected({ ids: [id], selected })
+  getCartList()
+}
+
+// 选中的商品列表
+const selectedData = computed(() => {
+  return cartList.value.map(item => {
+    if (item.selected) return item.id
+  }).filter(Boolean)
+})
+
+// 总结算金额
+const totalPrice = computed(() => {
+  return cartList.value.reduce((total, item) => {
+    if (item.selected) {
+      return total + item.sku.price * item.count
+    }
+    return total
+  }, 0)
+})
+
+// 总商品件数
+const totalCount = computed(() => {
+  return cartList.value.reduce((total, item) => {
+    if (item.selected) {
+      return total + item.count
+    }
+    return total
+  }, 0)
+})
+
+// 删除购物车中的商品
+const handleDelete = async (isAll, id) => {
+  Confirm(t('common.confirmTitle'), `是否确定删除${isAll? '选中的' : ''}商品?`).then(async () => {
+    const ids = isAll ? selectedData.value.join(',') : id
+    await deleteCartGoods(ids)
+    await getCartList()
+  })
+}
+
+// 商品数量更新
+const handleChangeCount = async (count, id) => {
+  await updateCartCount({ count, id })
+  await getCartList()
+}
+
+const confirmRef = ref()
+const handleSubmit = () => {
+  if (confirmRef.value) confirmRef.value.onConfirm()
+}
+
+const handleClose = () => {
+  showPay.value = false
+}
+const paySuccess = () => {
+  // 更新购物车列表
+  showPay.value = false
+  getCartList()
+}
+
+// 结算
+const showPay = ref(false)
+const selectedList = ref([])
+const skuInfo = ref(null) // 购买商品规格信息
+const handleSettlement = () => {
+  let items = []
+  let goods_list = [];
+  selectedList.value = cartList.value.filter((item) => selectedData.value.includes(item.id));
+  selectedList.value.map((item) => {
+    // 此处前端做出修改
+    items.push({
+      skuId: item.sku.id,
+      count: item.count,
+      cartId: item.id,
+      categoryId: item.spu.categoryId
+    })
+    goods_list.push({
+      goods_id: item.spu.id,
+      goods_num: item.count,
+    });
+  });
+  // return;
+  if (goods_list.length === 0) {
+    Snackbar.warning('请选择商品')
+    return
+  }
+  
+  skuInfo.value = JSON.stringify({
+    items
+  })
+  showPay.value = true
+}
+</script>
+
+<style scoped lang="scss">
+  :deep(.v-table.v-table--fixed-header > .v-table__wrapper > table > thead > tr > th) {
+    text-wrap: nowrap !important;
+    background-color: #f7f8fa !important;
+  }
+  :deep(.v-selection-control__input) {
+    color: var(--v-primary-base) !important;
+  }
+  :deep(.v-table.v-table--hover > .v-table__wrapper > table > tbody > tr > td) {
+    white-space: nowrap !important;
+  }
+</style>

+ 6 - 2
src/views/mall/components/GoodsItem/index.vue

@@ -7,7 +7,7 @@
       <div class="ml-5">
         <p class="font-size-15" :class="{'goods-name': showHover}">{{ item.spuName }}</p>
         <p class="color-999 font-size-14">{{ item.properties.map((property) => property.valueName).join(' ') }}</p>
-        <p>
+        <p v-if="showPriceCount">
           <span class="color-333">¥{{ fen2yuan(item.price) }}</span>
           <span v-if="item.count" class="color-999 font-size-13 ml-1">x {{ item.count }}</span>
         </p>
@@ -33,11 +33,15 @@ defineProps({
   showHover: {
     type: Boolean,
     default: true
+  },
+  showPriceCount: {
+    type: Boolean,
+    default: true
   }
 })
 
 const handleDetail = ({ id }) => {
-  window.open(`/mall/user/order/detail/${id}`)
+  window.open(`/mall/goodsDetail/${id}`)
 }
 </script>
 

+ 18 - 43
src/views/mall/components/details.vue

@@ -3,7 +3,10 @@
   <div class="default-width py-5" v-if="state.goodsInfo && Object.keys(state.goodsInfo).length">
     <div class="d-flex justify-space-between">
       <v-btn class="mb-3" size="large" color="primary" variant="text" prepend-icon="mdi-chevron-triple-left" @click="router.go(-1)">返回上一页</v-btn>
-      <v-btn class="mb-3" size="large" color="primary" variant="text" prepend-icon="mdi-account-circle-outline" to="/mall/user/order">我的订单</v-btn>
+      <div v-if="getToken()">
+        <v-btn class="mb-3" size="large" color="primary" variant="text" prepend-icon="mdi-account-circle-outline" to="/mall/user/order">我的订单</v-btn>
+        <v-btn class="mb-3 ml-1" size="large" color="primary" variant="text" prepend-icon="mdi-cart-outline" to="/mall/cart">购物车</v-btn>
+      </div>
     </div>
     <v-card class="carousel border-radius-8 white-bgc pa-5" style="width: 100%;">
       <div class=" d-flex">
@@ -44,6 +47,7 @@
             :goodsInfo="state.goodsInfo"
             @change="onSkuChange"
             @buy="onBuy"
+            @addCart="onAddCart"
           ></selectSku>
         </div>
       </div>
@@ -64,6 +68,7 @@
   </div>
   <!-- 快速登录 -->
   <loginPage v-if="showLogin" @loginSuccess="loginSuccess" @close="loginClose"></loginPage>
+  <!-- 结算 -->
   <CtDialog :visible="showPay" titleClass="text-h6" :widthType="3" title="订单信息" @submit="handleSubmit" @close="handleClose">
     <confirm ref="confirmRef" :data="skuInfo" @paySuccess="showPay = false"></confirm>
   </CtDialog>
@@ -72,6 +77,7 @@
 <script setup>
 defineOptions({name: 'goods-details'})
 import { getProductDetail } from '@/api/mall/product'
+import { addCart } from '@/api/mall/cart'
 import selectSku from './details/s-select-sku.vue'
 import describe from './details/describe.vue'
 import comment from './details/detail-comment-card.vue'
@@ -102,15 +108,6 @@ const getData = async () => {
   state.skeletonLoading = false;
   state.goodsInfo = obj
   showSelectSku.value = true
-  // // 加载是否收藏
-  // if (isLogin.value) {
-  //   FavoriteApi.isFavoriteExists(state.goodsId, 'goods').then((res) => {
-  //     if (res.code !== 0) {
-  //       return;
-  //     }
-  //     state.goodsInfo.favorite = res.data;
-  //   });
-  // }
 }
 getData()
 
@@ -146,46 +143,24 @@ const skuInfo = ref(null) // 购买商品规格信息
 const onBuy = async (e) => {
   if (!getToken()) return handleLogin()
   if (!e?.id) return Snackbar.warning('请选择商品规格!')
-  console.log('购买规格信息:', e)
+  // console.log('购买规格信息:', e)
   //
   skuInfo.value = JSON.stringify({
     items: [{ skuId: e.id, count: e.goods_num, categoryId: state.goodsInfo.categoryId, }]
   })
   showPay.value = true
 }
-// Snackbar.warning('购买功能暂未开放,敬请期待!')
 
-// onMounted(() => {
-//   onBuy({
-//     id: 45, 
-//     properties: [
-//         {
-//             propertyId: 19, 
-//             propertyName: "颜色", 
-//             valueId: 31, 
-//             valueName: "酒红色"
-//         }, 
-//         {
-//             propertyId: 21, 
-//             propertyName: "材质", 
-//             valueId: 33, 
-//             valueName: "纯棉"
-//         }
-//     ], 
-//     price: 2190, 
-//     marketPrice: 2190, 
-//     vipPrice: null, 
-//     picUrl: "http://menduner.citupro.com:6868/admin-api/infra/file/24/get/84d51e3ce5c466667c8cdf4b05c432b98a845559ef81461a78677c08a0608d62.jpg", 
-//     stock: 99, 
-//     weight: 0.1, 
-//     volume: 0.1, 
-//     value_id_array: [
-//         31, 
-//         33
-//     ], 
-//     goods_num: 1
-//   })
-// })
+// 添加购物车
+const onAddCart = async (e) => {
+  if (!e.id) {
+    Snackbar.warning('请选择商品规格')
+    return;
+  }
+  await addCart({ skuId: e.id, count: e.goods_num })
+  Snackbar.success('已添加到购物车~')
+  // 刷新购物车列表
+}
 
 const confirmRef = ref()
 const handleSubmit = () => {

+ 35 - 34
src/views/mall/components/details/comment-item.vue

@@ -4,14 +4,12 @@
     <!-- 用户评论 -->
     <div class="d-flex align-center mb-2">
       <div class="d-flexx mr-3">
-        <!-- <image class="avatar" :src="item.userAvatar"></image> -->
         <v-avatar class="avatar">
           <v-img alt="" :src="getUserAvatar(item.userAvatar, '1')" ></v-img>
         </v-avatar>
       </div>
       <div class="nickname mr-3 mb-1">{{ item.userNickname }}</div>
       <div class="">
-        <!-- <uni-rate :readonly="true" v-model="item.scores" size="18" /> -->
         <v-rating
           readonly
           :length="5"
@@ -25,19 +23,9 @@
       <div class="content"> {{ item.content }} </div>
       <div class="ss-m-t-24" v-if="item.picUrls?.length">
         <div class="scroll-box">
-          <div class="ss-flex">
-            <div v-for="(picUrl, index) in item.picUrls" :key="picUrl" class="ss-m-r-10">
-              <v-img :src="picUrl" :aspect-ratio="1" :current="index" style="border-radius: 8px;"></v-img>
-              <!-- <su-image
-                class="content-img"
-                isPreview
-                :previewList="item.picUrls"
-                :current="index"
-                :src="picUrl"
-                :height="120"
-                :width="120"
-                mode="aspectFill"
-              /> -->
+          <div class="d-flex">
+            <div v-for="(picUrl, index) in item.picUrls" :key="picUrl" class="mr-3" style="height: 100px; width: 100px;">
+              <v-img v-if="checkIsImage(picUrl)" :src="picUrl" :aspect-ratio="1" style="cursor: pointer;" @click="handleClick(index)"></v-img>
             </div>
           </div>
         </div>
@@ -49,57 +37,70 @@
       </div>
     </div>
   </div>
+  <PreviewImg v-if="showPreview" :current="current" :list="item.picUrls" @close="showPreview = !showPreview"></PreviewImg>
 </template>
 
 <script setup>
 import { getUserAvatar } from '@/utils/avatar'
-  const props = defineProps({
-    item: {
-      type: Object,
-      default() {},
-    },
-  });
+import { checkIsImage } from '@/utils'
+import { ref } from 'vue'
+const props = defineProps({
+  item: {
+    type: Object,
+    default() {},
+  }
+})
+
+// 预览
+const showPreview = ref(false)
+const current = ref(0)
+const handleClick = (index) => {
+  showPreview.value = !showPreview.value
+  current.value = index
+}
 </script>
 
 <style lang="scss" scoped>
   .avatar {
-    width: 52rpx;
-    height: 52rpx;
+    width: 40px;
+    height: 40px;
     border-radius: 50%;
   }
 
   .nickname {
-    font-size: 26rpx;
+    font-size: 14px;
     font-weight: 500;
     color: #999999;
   }
 
   .content {
-    width: 636rpx;
-    font-size: 26rpx;
+    width: 100%;
+    font-size: 14px;
     font-weight: 400;
     color: #333333;
   }
 
   .reply-box {
     background: #f8f8f8;
-    border-radius: 8rpx;
-    padding: 16rpx;
+    border-radius: 4px;
+    padding: 16px;
+    width: 100%;
   }
 
   .reply-title {
-    // position: absolute;
+    position: absolute;
     font-weight: 400;
-    font-size: 26rpx;
-    line-height: 40rpx;
+    font-size: 14px;
+    line-height: 32px;
     color: #333333;
   }
 
   .reply-content {
-    text-indent: 128rpx;
+    text-indent: 80px;
     font-weight: 400;
-    font-size: 26rpx;
-    line-height: 40rpx;
+    font-size: 14px;
+    line-height: 32px;
     color: #333333;
+    width: 100%;
   }
 </style>

+ 9 - 0
src/views/mall/components/details/describe.vue

@@ -16,4 +16,13 @@ const props = defineProps({
 // console.log('content:', props.content)
 </script>
 <style lang="scss" scoped>
+.detailsBox {
+  width: 100%;
+}
+:deep(img) {
+  max-width: 100%;
+}
+:deep(ol) {
+  margin: 0 20px;
+}
 </style>

+ 2 - 43
src/views/mall/components/details/detail-comment-card.vue

@@ -15,7 +15,7 @@
       <v-tabs v-model="currentTab" align-tabs="start" color="#ff8a04" @update:model-value="onTabsChange">
         <v-tab v-for="item of typeList" :key="item.name" :value="item.tab">{{ item.name }}</v-tab>
       </v-tabs>
-      <div class="card-content mt-5 px-3" style="min-height: 300px;">
+      <div class="card-content mt-8 px-3" style="min-height: 300px;">
         <template v-if="state.commentList.length">
           <div class="comment-box" v-for="item in state.commentList" :key="item.id">
             <comment-item :item="item" />
@@ -101,57 +101,16 @@
 
 <style lang="scss" scoped>
   .detail-comment-card {
-    margin: 0 20rpx 20rpx 20rpx;
-    padding: 20rpx 20rpx 0 20rpx;
     .card-header {
-      .line {
-        width: 6rpx;
-        height: 30rpx;
-        background: linear-gradient(180deg, var(--v-primary-base) 0%, var(--v-primary-lighten1) 100%);
-        border-radius: 3rpx;
-      }
-
-      .title {
-        font-size: 30rpx;
-        font-weight: bold;
-        line-height: normal;
-      }
-
       .des {
-        font-size: 24rpx;
         color: #999999;
       }
-
-      .more-btn {
-        font-size: 24rpx;
-        color: var(--v-primary-base);
-        line-height: normal;
-      }
-
-      .cicon-forward {
-        font-size: 24rpx;
-        line-height: normal;
-        color: var(--v-primary-base);
-        margin-top: 4rpx;
-      }
     }
   }
   .comment-box {
-    border-bottom: 2rpx solid #eeeeee;
+    // border-bottom: 1px solid #eeeeee;
     &:last-child {
       border: none;
     }
   }
-  .tabClass {
-    color: #666;
-    cursor: pointer;
-    font-size: 18px;
-    font-weight: 400;
-    letter-spacing: 0;
-    margin-right: 32px;
-  }
-  .tabClassAct {
-    color: #1a1a1a;
-    font-weight: bold;
-  }
 </style>

+ 3 - 8
src/views/mall/components/details/order/addressSelection.vue

@@ -13,8 +13,8 @@
               <div class="phone ml-3">{{ state.addressInfo.mobile }}</div>
             </div>
             <div class="d-flex mt-1">
-              <div class="default font-color" v-if="state.addressInfo.defaultStatus">[默认]</div>
-              <div class="line2 ml-3">
+              <div class="default font-color mr-3" v-if="state.addressInfo.defaultStatus">[默认]</div>
+              <div class="line2">
                 {{ state.addressInfo.areaName }} {{ state.addressInfo.detailAddress }}
               </div>
             </div>
@@ -22,14 +22,9 @@
           <div class="addressCon" v-else>
             <div class="setaddress">设置收货地址</div>
           </div>
-          <div class="iconfont">
-            <div class="ss-rest-button">
-              <div class="_icon-forward" />
-            </div>
-          </div>
         </div>
         <div class="mx-1">
-          <v-btn color="primary" variant="text" @click="onSelectAddress">切换</v-btn>
+          <v-btn color="primary" variant="text" @click="onSelectAddress">{{ state.addressInfo?.name ? '切换' : '去添加'}}</v-btn>
         </div>
       </div>
     </v-card>

+ 11 - 115
src/views/mall/components/details/order/confirm.vue

@@ -4,7 +4,7 @@
     <AddressSelection v-model="addressState" class="addressBox" />
     
     <!-- 购买的商品信息 -->
-    <div class="goodsListBox mb-3">
+    <v-card class="goodsListBox mb-3 pa-3">
       <s-goods-item
         v-for="(item, index) in state.orderInfo.items"
         :key="item.skuId"
@@ -13,20 +13,16 @@
         :skuText="item.properties.map((property) => property.valueName).join(' ')"
         :price="item.price"
         :num="item.count"
-        :marginTop="index ? '0' : '12'"
+        :style="{'marginTop': index ? '8px' : '0px'}"
       />
-    </div>
+    </v-card>
 
     <!-- 价格信息 -->
     <div>
       <div>
         <div class="order-item d-flex">
           <div class="item-title mr-3 ">商品金额:</div>
-          <div>
-            <span class="item-value">
-              ¥{{ fen2yuan(state.orderInfo.price.totalPrice) }}
-            </span>
-          </div>
+          <div>¥{{ fen2yuan(state.orderInfo.price.totalPrice) }}</div>
         </div>
         <!-- 快递配置时,信息的展示 -->
         <div
@@ -35,7 +31,7 @@
         >
           <div class="item-title mr-3">运{{ spaces() }}费:</div>
           <div>
-            <span class="item-value text-red" v-if="state.orderInfo.price.deliveryPrice > 0">
+            <span class="text-red" v-if="state.orderInfo.price.deliveryPrice > 0">
               +¥{{ fen2yuan(state.orderInfo.price.deliveryPrice) }}
             </span>
             <div class="item-value" v-else>免运费</div>
@@ -158,23 +154,6 @@
     await getOrderInfo()
   }
 
-
-  // ========== 积分 ==========
-  /**
-   * 使用积分抵扣
-   */
-  const changeIntegral = async () => {
-    state.pointStatus = !state.pointStatus;
-    await getOrderInfo();
-  };
-
-  // 选择优惠券
-  async function onSelectCoupon(couponId) {
-    state.orderPayload.couponId = couponId;
-    await getOrderInfo();
-    state.showCoupon = false;
-  }
-
   // 提交订单
   function onConfirm() {
     if (addressState.value.deliveryType === 1 && !addressState.value.addressInfo.id) {
@@ -218,10 +197,10 @@
       channelExtras: {},
     };
     const res = await payOrderSubmit(obj)
-    console.log('提交支付订单payOrderSubmit:', res)
-    Snackbar.warning('支付成功!')
+    // console.log('提交支付订单payOrderSubmit:', res)
+    Snackbar.success('支付成功,请前往我的订单查看!')
     emit('paySuccess')
-    userStore.getUserInfos()
+    userStore.getUserAccountBalance()
   }
 
   // 设置支付订单信息
@@ -281,7 +260,6 @@
     //     id: data.id,
     //   });
     // }
-
   }
 
   // 检查库存 & 计算订单价格
@@ -328,102 +306,20 @@
 
 <style lang="scss" scoped>
   .addressBox {
-    // border: 1px solid #000;
     margin-bottom: 20px;
   }
   .goodsListBox {
-    background: linear-gradient(to bottom, #e93323 0%, #e93323 100%);
+    // background: linear-gradient(to bottom, #e93323 0%, #e93323 100%);
     // background: linear-gradient(to bottom, var(--v-primary-base) 0%, var(--v-primary-base) 100%);
+    border-radius: 4px;
   }
-  .score-img {
-    width: 36rpx;
-    height: 36rpx;
-    margin: 0 4rpx;
-  }
-
   .order-item {
-    height: 80rpx;
-
-    .item-title {
-      font-size: 28rpx;
-      font-weight: 400;
-      color: #7a7a7a;
-    }
-
-    .item-value {
-      font-size: 28rpx;
-      font-weight: 500;
-      color: #7a7a7a;
-    }
-
-    .text-disabled {
-      color: #070606;
-    }
-
-    .item-icon {
-      color: #999;
-    }
-
-    .remark-input {
-      text-align: right;
-    }
-
-    .item-placeholder {
-      color: #999;
-      font-size: 26rpx;
-      text-align: right;
-    }
+    color: #7a7a7a;
   }
 
   .total-box-footer {
-    height: 90rpx;
-
     .total-num {
       color: #7a7a7a;
     }
   }
-
-  .footer-box {
-    height: 100rpx;
-
-    .submit-btn {
-      width: 240rpx;
-      height: 70rpx;
-      font-size: 28rpx;
-      font-weight: 500;
-
-      .goto-pay-text {
-        line-height: 28rpx;
-      }
-    }
-
-    .cancel-btn {
-      width: 240rpx;
-      height: 80rpx;
-      font-size: 26rpx;
-      background-color: #e5e5e5;
-      color: #999;
-    }
-  }
-
-  .title {
-    font-size: 36rpx;
-    font-weight: bold;
-    color: #333333;
-  }
-
-  .subtitle {
-    font-size: 28rpx;
-    color: #999999;
-  }
-
-  .cicon-checkbox {
-    font-size: 36rpx;
-    color: var(--v-primary-base);
-  }
-
-  .cicon-box {
-    font-size: 36rpx;
-    color: #999999;
-  }
 </style>

+ 14 - 13
src/views/mall/components/details/s-select-sku.vue

@@ -42,9 +42,10 @@
         </div>
       </div>
     </div>
-    <!-- 操作区 onAddCart onBuy -->
+    <!-- 操作区 -->
     <div>
       <v-btn class="buttons" color="primary" @click="onBuy">立即购买</v-btn>
+      <v-btn class="ml-3 px-8" color="warning" @click="onAddCart">加入购物车</v-btn>
     </div>
   </div>
 </template>
@@ -100,18 +101,18 @@ const props = defineProps({
   }
 
   // 加入购物车
-  // function onAddCart() {
-  //   if (state.selectedSku.id <= 0) {
-  //     Snackbar.warning('请选择商品规格')
-  //     return;
-  //   }
-  //   if (state.selectedSku.stock <= 0) {
-  //     Snackbar.warning('库存不足')
-  //     return;
-  //   }
-
-  //   emits('addCart', state.selectedSku);
-  // }
+  function onAddCart() {
+    if (state.selectedSku.id <= 0) {
+      Snackbar.warning('请选择商品规格')
+      return;
+    }
+    if (state.selectedSku.stock <= 0) {
+      Snackbar.warning('库存不足')
+      return;
+    }
+
+    emits('addCart', state.selectedSku);
+  }
 
   // 立即购买
   function onBuy() {

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

@@ -11,7 +11,7 @@
           <v-icon>mdi-octagram-outline</v-icon>
           积分兑换
         </span> -->
-        <span class="cursor-">
+        <span class="cursor-pointer" :class="{'active-route' : isActive('/mall/cart')}" @click="handleTo('/mall/cart')">
           <v-icon size="20">mdi-cart-outline</v-icon>
           购物车
         </span>

+ 71 - 0
src/views/mall/components/prizeDraw.vue

@@ -0,0 +1,71 @@
+<template>
+  <LuckyGrid
+    ref="myLucky"
+    width="300px"
+    height="300px"
+    :prizes="prizes"
+    :blocks="blocks"
+    :buttons="buttons"
+    :activeStyle="activeStyle"
+    :defaultStyle="defaultStyle"
+    @start="startCallback"
+    @end="endCallback"
+  />
+  <v-btn @click="startCallback" class="mt-3" color="primary">开始抽奖</v-btn>
+</template>
+
+<script setup>
+defineOptions({ name: 'prizeDraw'})
+import { ref } from 'vue'
+
+const myLucky = ref()
+const blocks = [
+  { padding: '10px', background: '#869cfa' },
+  { padding: '10px', background: '#e9e8fe' },
+]
+const prizes = [
+  { x: 0, y: 0 },
+  { x: 1, y: 0 },
+  { x: 2, y: 0 },
+  { x: 2, y: 1 },
+  { x: 2, y: 2 },
+  { x: 1, y: 2 },
+  { x: 0, y: 2 },
+  { x: 0, y: 1 }
+]
+const buttons = [
+  {
+    x: 1, y: 1,
+    background: '#fe9202',
+    borderRaduis: '10px',
+    fonts: [{ text: '开始抽奖', fontColor: '#fff', top: 30}]
+  }
+]
+const defaultStyle = {
+  background: '#b8c5f2'
+}
+const activeStyle = {
+  fontColor: 'red',
+  background: '#869cfa'
+}
+
+const startCallback = () => {
+  // 调用抽奖组件的play方法开始游戏
+  myLucky.value.play()
+  // 模拟调用接口异步抽奖
+  setTimeout(() => {
+    // 假设后端返回的中奖索引是0
+    const index = 3
+    // 调用stop停止旋转并传递中奖索引
+    myLucky.value.stop(index)
+  }, 1500)
+}
+
+const endCallback  = (prize) => {
+  console.log(prize, 'end')
+}
+</script>
+
+<style scoped lang="scss">
+
+</style>

+ 5 - 6
src/views/mall/components/s-goods-item/index.vue

@@ -1,9 +1,6 @@
 <template>
   <div class="box">
-    <div
-      class="d-flex"
-      :style="[{ borderRadius: radius + 'px', marginTop: marginTop + 'px' }]"
-    >
+    <div class="d-flex">
       <div style="width: 80px; height: 80px;" class="mr-3">
         <v-img :src="img" :aspect-ratio="1" style="border-radius: 8px;"></v-img>
       </div>
@@ -37,7 +34,7 @@
    *
    * @property {String} img 											- 图片
    * @property {String} title 										- 标题
-   * @property {Number} titleWidth = 0								- 标题宽度,默认0,单位rpx
+   * @property {Number} titleWidth = 0								- 标题宽度,默认0,单位
    * @property {String} skuText 										- 规格
    * @property {String | Number} price 								- 价格
    * @property {String} priceColor 									- 价格颜色
@@ -99,9 +96,11 @@
 
 <style lang="scss" scoped>
 .box {
-  background-color: var(--default-bgc);
+  // background-color: var(--default-bgc);
+  background-color: #e9e9e9;
   border-radius: 8px;
   padding: 12px;
+  // border: 1px dashed #000000;
 }
 .buyNum {
   color: #777;

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

@@ -67,7 +67,6 @@ const props = defineProps({
     default: false,
   }
 })
-console.log('showSelect:', props.showSelect)
 
 const isAdd = ref(true)
 const editId = ref(null)

+ 2 - 1
src/views/publicRecruitment/components/table.vue

@@ -27,6 +27,7 @@
 <script setup>
 defineOptions({ name: 'myPublicRecruitment-table-page'})
 import { timesTampChange } from '@/utils/date'
+import { dealEnterpriseName } from '@/utils/getText'
 import { commissionCalculation } from '@/utils/position'
 
 const emit = defineEmits(['page'])
@@ -39,7 +40,7 @@ defineProps({
 
 const headers = [
   { title: '牛人', value: 'sendPerson.name', key: 'name', sortable: false },
-  { title: '应聘公司', key: 'enterprise.anotherName', sortable: false },
+  { title: '应聘公司', key: 'enterprise.anotherName', sortable: false, value: item => dealEnterpriseName(item.enterprise.anotherName || item.enterprise.name) },
   { title: '应聘职位', key: 'job', value: item => item?.job?.name, sortable: false },
   { title: '赏金', key: 'hirePrice', sortable: false },
   { title: '岗位薪资', key: 'salary', value: item => item?.job?.payFrom && item?.job?.payTo ? `${item?.job?.payFrom ? item?.job?.payFrom + '-' : ''}${item?.job?.payTo}` : '面议', sortable: false },

+ 2 - 1
src/views/recruit/components/message/components/chatting.vue

@@ -18,7 +18,7 @@
           <template v-if="info.enterpriseId">
             <span>{{ info.postNameCn }}</span>
             <span v-if="info.postNameCn && info.enterpriseAnotherName" class="septal-line"></span>
-            <span>{{ info.enterpriseAnotherName }}</span>
+            <span>{{ dealEnterpriseName(info.enterpriseAnotherName) }}</span>
           </template>
         </p>
       </div>
@@ -240,6 +240,7 @@ import { useI18n } from '@/hooks/web/useI18n'
 import { useRouter } from 'vue-router';
 import { getDict } from '@/hooks/web/useDictionaries'
 import { getUserAvatar } from '@/utils/avatar'
+import { dealEnterpriseName } from '@/utils/getText'
 
 import { useUserStore } from '@/store/user'
 const isEnterprise = inject('isEnterprise')

+ 2 - 1
src/views/recruit/components/message/index.vue

@@ -46,7 +46,7 @@
                 <div v-if="!isEnterprise" class="mt-2 d-flex align-center">
                   {{ title }}
                   <div class="ml-3 color-666 font-size-14 enterprise-name ellipsis" :style="{'color': val.channel.channelID === info?.channel?.channelID ? '#00897B' : '#666'}">
-                    {{ val.userInfoVo?.userInfoResp?.enterpriseAnotherName }}
+                    {{ dealEnterpriseName(val.userInfoVo?.userInfoResp?.enterpriseAnotherName) }}
                     <span class="line" v-if="val.userInfoVo?.userInfoResp?.postNameCn && val.userInfoVo?.userInfoResp?.enterpriseAnotherName"></span>
                     {{ val.userInfoVo?.userInfoResp?.postNameCn }}
                   </div>
@@ -189,6 +189,7 @@ import { saveInterviewInvite } from '@/api/recruit/enterprise/interview'
 import { savePersonResumeCv } from '@/api/recruit/personal/resume'
 import { userInterviewInviteReject, userInterviewInviteConsent } from '@/api/recruit/personal/personalCenter'
 import { getPersonResumeCv } from '@/api/recruit/personal/resume'
+import { dealEnterpriseName } from '@/utils/getText'
 
 import { useIMStore } from '@/store/im'
 import { useUserStore } from '@/store/user'

+ 4 - 2
src/views/recruit/enterprise/systemManagement/groupAccount/components/record.vue

@@ -23,7 +23,9 @@ import { ref } from 'vue'
 import { useI18n } from '@/hooks/web/useI18n'; const { t } = useI18n()
 import { timesTampChange } from '@/utils/date'
 import { enterpriseInviteRecordPage } from '@/api/recruit/enterprise/enterpriseInvite.js'
+import { dealEnterpriseName } from '@/utils/getText';
 defineOptions({name: 'groupAccount-component-record'})
+
 const props = defineProps({
   inviteType: {
     type: [String, Number],
@@ -58,9 +60,9 @@ const getData = async () => {
     const { list, total: number } = await enterpriseInviteRecordPage(query.value)
     tableData.value = list.map(e => {
       const showInfo = {
-        currentAccountEnterpriseAnotherName: info?.enterpriseAnotherName || '--',
+        currentAccountEnterpriseAnotherName: dealEnterpriseName(info?.enterpriseAnotherName) || '--',
         currentAccountUserName: info?.name || '--',
-        name: props.inviteType - 0 ? e?.enterpriseUser?.enterpriseName : e?.enterpriseUser?.name,
+        name: props.inviteType - 0 ? dealEnterpriseName(e?.enterpriseUser?.enterpriseName) : e?.enterpriseUser?.name,
         time: e.createTime ? timesTampChange(e.createTime) : '--',
       }
       return { showInfo, ...e }

+ 4 - 4
src/views/recruit/enterprise/systemManagement/groupAccount/index.vue

@@ -18,8 +18,8 @@
         >
           <template v-slot:title="{ item }">
             <div class="treeTitle font-size-15">
-              {{ item.anotherName || item.name }}
-              <v-tooltip activator="parent" location="end">{{ item.anotherName || item.name }}</v-tooltip>
+              {{ dealEnterpriseName(item.anotherName || item.name) }}
+              <v-tooltip activator="parent" location="end">{{ dealEnterpriseName(item.anotherName || item.name) }}</v-tooltip>
             </div>
           </template>
         </v-treeview>
@@ -107,10 +107,10 @@ import Confirm from '@/plugins/confirm'
 import Snackbar from '@/plugins/snackbar'
 import { checkCompanyEmail } from '@/utils/validate'
 import { updateGroupUserAccount } from '@/api/enterprise'
-import { getDict } from '@/hooks/web/useDictionaries'
 import { getUserAvatar } from '@/utils/avatar'
 import { uploadFile } from '@/api/common'
 import { getToken } from '@/utils/auth'
+import { dealEnterpriseName } from '@/utils/getText';
 
 const { t } = useI18n()
 const showBadge = ref(false) // 性别设置甲方要求已去掉
@@ -126,7 +126,7 @@ const tableData = ref([])
 const treeData = ref([])
 const headers = [
   { title: t('login.username'), key: 'name', sortable: false },
-  { title: t('enterprise.userManagement.affiliatedEnterprise'), key: 'enterpriseAnotherName', sortable: false, value: item => item.enterpriseAnotherName || item.enterpriseName },
+  { title: t('enterprise.userManagement.affiliatedEnterprise'), key: 'enterpriseAnotherName', sortable: false, value: item => dealEnterpriseName(item.enterpriseAnotherName || item.enterpriseName) },
   { title: t('enterprise.userManagement.post'), key: 'postName', sortable: false },
   { title: t('enterprise.userManagement.phone'), key: 'phone', sortable: false },
   { title: t('enterprise.userManagement.email'), key: 'email', sortable: false },

+ 2 - 1
src/views/recruit/personal/PersonalCenter/jobFeedback/components/interview/item.vue

@@ -39,7 +39,7 @@
               <v-img width="30" height="30" :src="val.enterprise.logoUrl || 'https://minio.citupro.com/dev/menduner/company-avatar.png'"></v-img>
             </div>
             <div class="ellipsis" style="max-width: 400px;">
-              <span class="mx-2 enterprise-name" @click="handleToEnterprise(val)">{{ val.enterprise.anotherName }}</span>
+              <span class="mx-2 enterprise-name" @click="handleToEnterprise(val)">{{ dealEnterpriseName(val.enterprise.anotherName || val.enterprise.name) }}</span>
             [
               <span>{{ val.enterprise.industryName }}</span>
               <span>&nbsp;·&nbsp;{{ val.enterprise.scaleName }}</span>
@@ -60,6 +60,7 @@ import Snackbar from '@/plugins/snackbar'
 import Confirm from '@/plugins/confirm'
 import { getUserAvatar } from '@/utils/avatar'
 import { useRouter } from 'vue-router'
+import { dealEnterpriseName } from '@/utils/getText'
 
 const { t } = useI18n()
 const emits = defineEmits(['refresh'])

+ 2 - 1
src/views/recruit/personal/PersonalCenter/jobFeedback/components/seenMe.vue

@@ -18,7 +18,7 @@
                 <v-img :src="item?.enterprise.logoUrl || 'https://minio.citupro.com/dev/menduner/company-avatar.png'" :alt="item.enterprise.anotherName" :width="40" style="height: 40px;border-radius: 4px;"/>
               </div>
               <div class="company-info">
-                <h3 :class="{'default-active': item.active }" class="title1 cursor-pointer">{{ item.enterprise.anotherName }}</h3>
+                <h3 :class="{'default-active': item.active }" class="title1 cursor-pointer">{{ dealEnterpriseName(item.enterprise.anotherName || item.enterprise.name) }}</h3>
                 <p class="mt-2">{{ item.enterprise.scaleName }}<span class="mx-2">|</span>{{ item.enterprise.industryName }}</p>
               </div>
             </div>
@@ -58,6 +58,7 @@ import Empty from '@/components/Empty'
 import { getUserAvatar } from '@/utils/avatar'
 import { useRouter } from 'vue-router'
 import { useUserStore } from '@/store/user'
+import { dealEnterpriseName } from '@/utils/getText'
 
 const router = useRouter()
 const total = ref(0)

+ 3 - 1
src/views/recruit/personal/company/components/companyItem.vue

@@ -6,7 +6,7 @@
           <v-img :src="item.enterprise.logoUrl || 'https://minio.citupro.com/dev/menduner/company-avatar.png'" :alt="item.enterprise.anotherName" :width="40" style="height: 40px;border-radius: 4px;"/>
         </div>
         <div class="company-info">
-          <h3 :class="{'default-active': item.active }" style="width: 200px;">{{ item.enterprise.anotherName }}</h3>
+          <h3 :class="{'default-active': item.active }" style="width: 200px;">{{ dealEnterpriseName(item.enterprise.anotherName || item.enterprise.name) }}</h3>
           <p>{{ item.enterprise.industryName }}</p>
         </div>
       </div>
@@ -31,6 +31,8 @@
 </template>
 
 <script setup>
+import { dealEnterpriseName } from '@/utils/getText'
+
 defineOptions({ name: 'company-item'})
 defineProps({
   list: Array

+ 2 - 1
src/views/recruit/personal/companyDetail/index.vue

@@ -6,7 +6,7 @@
           <v-img width="60" height="60" :src="info.enterprise.logoUrl || 'https://minio.citupro.com/dev/menduner/company-avatar.png'"></v-img>
           <div class="ml-4">
             <div class="contact-name">
-              {{ info.enterprise.name }}
+              {{ dealEnterpriseName(info.enterprise.anotherName || info.enterprise.name) }}
             </div>
             <div class="contact-info">
               {{ info.scaleName }}
@@ -81,6 +81,7 @@ import { useRoute, useRouter } from 'vue-router'
 import { getToken } from '@/utils/auth'
 import Snackbar from '@/plugins/snackbar'
 import loginPage from '@/views/common/loginDialog.vue'
+import { dealEnterpriseName } from '@/utils/getText'
 
 const route = useRoute()
 const router = useRouter()

+ 1 - 1
src/views/recruit/personal/home/components/advertisement/dynamic/intercontinental.vue

@@ -7,7 +7,7 @@
       </v-carousel>
       <v-img class="logo-png" src="https://minio.citupro.com/dev/menduner/preferredGroup/IHG-logo.png" width="152" height="72"></v-img>
     </div> -->
-    <v-img src="https://minio.menduner.com/dev/menduner/IHG.gif" lazy-src="https://minio.menduner.com/dev/menduner/IHG.gif" width="1184" height="500">
+    <v-img src="https://minio.menduner.com/dev/9e3d57525496061ed931ee0d6a91c123561ab30a3f22c55a812a66f40d9a281b.gif" lazy-src="https://minio.menduner.com/dev/9e3d57525496061ed931ee0d6a91c123561ab30a3f22c55a812a66f40d9a281b.gif" width="1184" height="500">
       <template v-slot:placeholder>
         <v-row align="center" class="fill-height ma-0" justify="center">
           <v-progress-circular color="grey-lighten-5" indeterminate></v-progress-circular>

+ 2 - 1
src/views/recruit/personal/position/components/details.vue

@@ -64,7 +64,7 @@
                 <div class="ml-2">
                   <div class="contact-name">{{ info.contact.name }}</div>
                   <div class="contact-info">
-                    {{ info.enterprise.anotherName }}
+                    {{ dealEnterpriseName(info.enterprise.anotherName || info.enterprise.name) }}
                      <span v-if="info?.enterprise?.anotherName && info?.contact?.postNameCn">·</span>
                     {{ info.contact.postNameCn }}</div>
                 </div>
@@ -188,6 +188,7 @@ import { getToken } from '@/utils/auth'
 import { getUserAvatar } from '@/utils/avatar'
 import { checkPersonBaseInfo } from '@/utils/check'
 import dialogExtend from '@/plugins/dialogExtend'
+import { dealEnterpriseName } from '@/utils/getText'
 
 const props = defineProps({
   defaultWidth: {

+ 2 - 1
src/views/recruit/personal/recommend/components/positionList.vue

@@ -38,7 +38,7 @@
                 <v-img :src="item.logoUrl || 'https://minio.citupro.com/dev/menduner/company-avatar.png'" />
               </v-avatar>
               <span class="names ml-2 font-size-14 ellipsis" style="max-width: 88%;">
-                {{ item.anotherName }}
+                {{ dealEnterpriseName(item.anotherName || item.name) }}
                 <span class="color-999 font-size-13 ml-3">
                   <span>{{ item.industryName }}</span>
                   <span class="septal-line" v-if="item.industryName && item.scaleName"></span>
@@ -56,6 +56,7 @@
 <script setup>
 defineOptions({ name: 'position-card-item' })
 import { ref, watch } from 'vue'
+import { dealEnterpriseName } from '@/utils/getText'
 
 const emit = defineEmits([''])
 const props = defineProps({

File diff suppressed because it is too large
+ 108 - 0
vite.config.mjs.timestamp-1735030220221-ec7db8c3a2187.mjs


Some files were not shown because too many files changed in this diff