Browse Source

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

lifanagju_citu 7 months ago
parent
commit
6796c14f8e

+ 1 - 1
components.d.ts

@@ -30,9 +30,9 @@ declare module 'vue' {
     DatePicker: typeof import('./src/components/DatePicker/index.vue')['default']
     DatePicker: typeof import('./src/components/DatePicker/index.vue')['default']
     Details: typeof import('./src/components/Enterprise/details.vue')['default']
     Details: typeof import('./src/components/Enterprise/details.vue')['default']
     Echarts: typeof import('./src/components/Echarts/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']
     ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
     ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
     ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
-    ElTimePicker: typeof import('element-plus/es')['ElTimePicker']
     Empty: typeof import('./src/components/Empty/index.vue')['default']
     Empty: typeof import('./src/components/Empty/index.vue')['default']
     File: typeof import('./src/components/Upload/file.vue')['default']
     File: typeof import('./src/components/Upload/file.vue')['default']
     HeadSearch: typeof import('./src/components/headSearch/index.vue')['default']
     HeadSearch: typeof import('./src/components/headSearch/index.vue')['default']

+ 17 - 0
src/api/mall.js

@@ -0,0 +1,17 @@
+import request from '@/config/axios'
+
+// 提交积分商品兑换
+export const redeemSubmit = async (data) => {
+  return request.post({
+    url: '/app-api/menduner/system/redeem/submit',
+    data
+  })
+}
+
+// 获取兑换记录
+export const getRedeemPage = async (params) => {
+  return request.get({
+    url: '/app-api/menduner/system/redeem/page',
+    params
+  })
+}

+ 7 - 0
src/api/position.js

@@ -236,6 +236,13 @@ export const topJobAdvertised = async (ids) => {
   })
   })
 }
 }
 
 
+// 招聘端-取消置顶职位
+export const topJobAdvertisedCancel = async (ids) => {
+  return await request.post({
+    url: `/app-api/menduner/system/recruit/job-advertised/un-top?ids=${ids}`
+  })
+}
+
 // 招聘端-职位详情-认证情况
 // 招聘端-职位详情-认证情况
 export const getEnterpriseAuthDetails = async (enterpriseId) => {
 export const getEnterpriseAuthDetails = async (enterpriseId) => {
   return await request.get({
   return await request.get({

File diff suppressed because it is too large
+ 1 - 0
src/assets/svg/top.svg


+ 11 - 0
src/router/modules/components/recruit/personCenter.js

@@ -77,6 +77,17 @@ const personCenter = [
             },
             },
             component: () => import('@/views/recruit/personal/PersonalCenter/wallet/index.vue'),
             component: () => import('@/views/recruit/personal/PersonalCenter/wallet/index.vue'),
           },
           },
+          // 交易订单
+          {
+            path: '/recruit/personal/personalCenter/tradeOrder',
+            name: 'Trading order',
+            component: () => import('@/views/recruit/personal/PersonalCenter/tradeOrder/index.vue'),
+            meta: {
+              title: '交易订单',
+              enName: 'Trading order',
+              icon: 'mdi-order-bool-ascending-variant'
+            }
+          },
           // 会员福利
           // 会员福利
           {
           {
             path: '/recruit/personal/personalCenter/memberBenefits',
             path: '/recruit/personal/personalCenter/memberBenefits',

+ 87 - 52
src/views/mall/exchange.vue

@@ -3,85 +3,120 @@
   <div class="listBox">
   <div class="listBox">
     <div v-for="(item, index) in dataList" :key="'exchange' + index">
     <div v-for="(item, index) in dataList" :key="'exchange' + index">
       <div class="cursor-pointer mx-5 mb-4" style="width: 180px;" @click="handleShowDetail(item)">
       <div class="cursor-pointer mx-5 mb-4" style="width: 180px;" @click="handleShowDetail(item)">
-        <v-img width="180" height="180" :src="item.图片 || 'https://minio.citupro.com/dev/menduner/7.png'"></v-img>
-        <div class="ellipsis mt-2" style="font-size: 14px;">{{ item.物品名称 }}</div>
-        <div class="ellipsis mt-1" style="font-size: 13px;">消耗积分<span class="ml-1" style="color: var(--v-primary-base)">{{ item.消耗积分 }}</span></div>
+        <v-img width="180" height="180" :src="item.url || 'https://minio.citupro.com/dev/menduner/7.png'"></v-img>
+        <div class="ellipsis mt-2" style="font-size: 14px;">{{ item.name }}</div>
+        <div class="ellipsis mt-1" style="font-size: 13px;">消耗积分<span class="ml-1" style="color: var(--v-primary-base)">{{ item.point }}</span></div>
       </div>
       </div>
     </div>
     </div>
   </div>
   </div>
-  <Dialog :visible="showDetail" titleClass="text-h6" :widthType="3" title="详情说明" @submit="handleSubmit" @close="showDetail = false">
-    <div>
-      <!-- <span style="font-size: 15px;">物品名称:</span> -->
-      <span style="font-size: 14px;color: red;">{{ detailItem.物品名称 }}</span>
-    </div>
+
+  <Dialog :visible="showDetail" titleClass="text-h6" :footer="point > detailItem?.point" :widthType="3" title="详情说明" @submit="handleSubmit" @close="showDetail = false">
+    <div class="color-primary font-size-20">{{ detailItem.name }}</div>
     <div class="tips">
     <div class="tips">
-      <div>使用规则:</div>
-      <span>兑换时,优先使用即将到期积分进行兑换</span>
-      <div>使用时间:</div>
-      <span>使用时间为当前招聘通期限内都可以使用, 超出当前招聘通期限不可以使用</span>
-      <div>使用说明:</div>
-      <span>积分一经兑换概不退回,敬请谅解。</span>
+      <div>使用说明:积分一经兑换概不退回,敬请谅解。</div>
     </div>
     </div>
     <div class="d-flex align-center my-5">
     <div class="d-flex align-center my-5">
-      <div class="mr-5">兑换数量</div>
-      <textUI class="mr-5" v-model="num" :item="textItem" @change="handleChange"></textUI>
       <div class="mr-1">消耗积分</div>
       <div class="mr-1">消耗积分</div>
-      <div style="color: var(--v-primary-base);">{{ (detailItem.消耗积分- 0)*num }}</div>
+      <div class="color-primary">{{ detailItem.point }}</div>
+    </div>
+    <div v-if="point > detailItem.point">
+      <div class="color-666 mb-5 mt-10">{{ detailItem?.type  ? '收货人信息填写' : '联系电话填写' }}</div>
+      <CtForm ref="CtFormRef" :items="formItems"></CtForm>
     </div>
     </div>
-    <div v-if="(detailItem.消耗积分- 0)*num > 135" style="font-size: 14px;color: red;">积分不足,快去赚取积分吧</div>
+    <div v-if="point < detailItem.point" class="text-end color-error font-size-14 cursor-pointer text-decoration-underline" @click="emit('toTaskCenter')">积分不足,快去赚取积分吧</div>
   </Dialog>
   </Dialog>
 </template>
 </template>
 
 
 <script setup>
 <script setup>
-import Dialog from '@/components/CtDialog'
-import textUI from '@/components/FormUI/TextInput'
-// import Snackbar from '@/plugins/snackbar'
-import { ref } from 'vue'
 defineOptions({name: 'mall-exchange'})
 defineOptions({name: 'mall-exchange'})
+import { ref } from 'vue'
+import { getToken } from '@/utils/auth'
+import Dialog from '@/components/CtDialog'
+import Snackbar from '@/plugins/snackbar'
+import { redeemSubmit } from '@/api/mall'
+import { useUserStore } from '@/store/user'
 
 
-// 数据
-const dataList = ref([])
-const getData = () => {
-  dataList.value = [
-    { 物品名称: '房券', 消耗积分: '12000', 图片: 'http://www.51lip.com/public/images/17/64/c2/bd52d2072e4304a43d383069fe63d15cb12fddf6.jpg' },
-    { 物品名称: '门墩儿酒店英语学习年卡', 消耗积分: '8000', 图片: 'http://www.51lip.com/public/images/72/9b/de/42d8056bdd889217793f84cb68936d5682ea72dc.png' },
-    { 物品名称: '红酒', 消耗积分: '5000', 图片: 'http://www.51lip.com/public/images/34/fb/dd/dfc8757b4204fc79352490478e6be8ae71fd748a.jpg' },
-    { 物品名称: '瑞幸电子咖啡券', 消耗积分: '2000', 图片: 'http://www.51lip.com/public/images/68/0a/f4/53d5f128b19d22c04ed4538360ae0db0a388f5f8.jpg' },
-    { 物品名称: '减压捏捏乐', 消耗积分: '500', 图片: 'http://www.51lip.com/public/images/07/4c/e3/0917d02b646e9a5eeec427f70197d4915007ddbe.jpg' },
-    // { 物品名称: '幻响小宜多功能照明灯 led台灯', 消耗积分: '88888', 图片: 'http://www.51lip.com/public/images/2e/75/36/6c6fae90c1e7cf134a704dbcb917d2bdc9ac2805.jpg' },
-    // { 物品名称: '眼罩颈枕', 消耗积分: '10080', 图片: 'http://www.51lip.com/public/images/2f/e1/99/1a861d35c0e7358633f548d6f142b92d5ec2f6cb.jpg' },
-    // { 物品名称: 'PGG 腰果按摩枕W8', 消耗积分: '25000', 图片: 'http://www.51lip.com/public/images/f0/66/e2/6f9b0b756352324af411e4e4b36fed9f71c078d9.jpg' },
-    // { 物品名称: '现代酒店管理实用教程电子书', 消耗积分: '100', 图片: 'https://img2.baidu.com/it/u=153650066,2494782747&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=704' },
-    // { 物品名称: '星巴克美式一杯', 消耗积分: '500', 图片: 'https://file-ve.veimg.cn/files/2024/05/2024052714240147ow0eijdnjxg.jpg' },
+const emit = defineEmits(['login', 'toTaskCenter'])
+defineProps({
+  point: {
+    type: Number,
+    default: 0
+  }
+})
+
+const CtFormRef = ref()
+const formItems = ref({
+  options: [
+    {
+      type: 'text',
+      key: 'contactName',
+      value: localStorage.getItem('baseInfo') ? JSON.parse(localStorage.getItem('baseInfo')).name : '',
+      hide: false,
+      label: '收货人姓名 *',
+      rules: [v => !!v || '请输入收货人姓名'],
+    },
+    {
+      type: 'text',
+      key: 'contactPhone',
+      value: localStorage.getItem('userInfo') ? JSON.parse(localStorage.getItem('userInfo')).phone : '',
+      label: '收货人联系电话 *',
+      outlined: true,
+      rules: [v => !!v || '请填写收货人联系电话']
+    },
+    {
+      type: 'textarea',
+      key: 'contactAddress',
+      value: '先烈中路100号大院203室',
+      hide: false,
+      label: '收获详细地址 *',
+      rules: [v => !!v || '请填写收获详细地址']
+    }
   ]
   ]
-}
-getData()
+})
+
+// 数据
+const dataList = ref([
+  { name: '房券', point: 12000, url: 'http://www.51lip.com/public/images/17/64/c2/bd52d2072e4304a43d383069fe63d15cb12fddf6.jpg', type: 1 },
+  { name: '门墩儿酒店英语学习年卡', point: 8000, url: 'http://www.51lip.com/public/images/72/9b/de/42d8056bdd889217793f84cb68936d5682ea72dc.png', type: 0 },
+  { name: '红酒', point: 5000, url: 'http://www.51lip.com/public/images/34/fb/dd/dfc8757b4204fc79352490478e6be8ae71fd748a.jpg', type: 1 },
+  { name: '瑞幸电子咖啡券', point: 2000, url: 'http://www.51lip.com/public/images/68/0a/f4/53d5f128b19d22c04ed4538360ae0db0a388f5f8.jpg', type: 0 },
+  { name: '减压捏捏乐', point: 500, url: 'http://www.51lip.com/public/images/07/4c/e3/0917d02b646e9a5eeec427f70197d4915007ddbe.jpg', type: 1 }
+])
 
 
 // 详情说明弹窗
 // 详情说明弹窗
 const showDetail = ref(false)
 const showDetail = ref(false)
-const detailItem = ref(null)
+const detailItem = ref({})
 const handleShowDetail = (item) =>{
 const handleShowDetail = (item) =>{
+  if (!getToken()) {
+    Snackbar.warning('请先登录')
+    emit('login')
+    return
+  }
   detailItem.value = item
   detailItem.value = item
+  formItems.value.options.forEach(e => {
+    if (e.key !== 'contactPhone') e.hide = item.type ? false : true
+  })
   showDetail.value = true
   showDetail.value = true
 }
 }
 
 
 // 兑换提交
 // 兑换提交
 const handleSubmit = async () =>{
 const handleSubmit = async () =>{
-}
-
-// 兑换数量
-const num = ref(1)
-const textItem = ref({
-  type: 'number',
-  value: num.value,
-  width: 80,
-  hideDetails: true,
-  // density: 'compact',
-  // label: '请输入自定义标签',
-})
-const handleChange = async () =>{
+  const { valid } = await CtFormRef.value.formRef.validate()
+  if (!valid) return
+  const obj = {
+    ...detailItem.value
+  }
+  formItems.value.options.forEach(e => {
+    obj[e.key] = e.value
+  })
+  await redeemSubmit(obj)
+  Snackbar.success('提交成功')
+  showDetail.value = false
+  detailItem.value = {}
+  await useUserStore().getUserAccountInfo()
 }
 }
 </script>
 </script>
+
 <style lang="scss" scoped>
 <style lang="scss" scoped>
 .listBox {
 .listBox {
   display: flex;
   display: flex;

+ 41 - 12
src/views/mall/exchangeRecords.vue

@@ -1,27 +1,56 @@
 <!-- 积分兑换记录 -->
 <!-- 积分兑换记录 -->
 <template>
 <template>
   <div>
   <div>
-    <TablePage :items="dataList"></TablePage>
+    <CtTable
+      class="mt-3"
+      :items="dataList"
+      :headers="headers"
+      :loading="false"
+      :elevation="0"
+      :isTools="false"
+      :showPage="true"
+      :total="total"
+      :page-info="queryParams"
+      itemKey="id"
+      @pageHandleChange="handleChangePage"
+    >
+    </CtTable>
   </div>
   </div>
 </template>
 </template>
 
 
 <script setup>
 <script setup>
-import TablePage from './components/table.vue'
-import { ref } from 'vue'
 defineOptions({name: 'mall-exchangeRecords'})
 defineOptions({name: 'mall-exchangeRecords'})
+import { ref } from 'vue'
+import { getRedeemPage } from '@/api/mall'
+import { getToken } from '@/utils/auth'
+
+const total = ref(0)
+const headers = [
+  { title: '商品名称', key: 'name', sortable: false },
+  { title: '消耗积分', key: 'point', sortable: false },
+  { title: '收货人姓名', key: 'contactName', sortable: false },
+  { title: '联系电话/收货人电话', key: 'contactPhone', sortable: false },
+  { title: '收货详细地址', key: 'contactAddress', sortable: false }
+]
+const queryParams = ref({
+  pageNo: 1,
+  pageSize: 10,
+})
 
 
 // 数据
 // 数据
 const dataList = ref([])
 const dataList = ref([])
-const getData = () => {
-  dataList.value = [
-    // { 兑换物品: '刷新简历1次体验卡', 兑换时间: '2024-06-20 20:00', 消耗积分: '500' },
-    // { 兑换物品: '会员3天体验卡', 兑换时间: '2024-06-20 00:00', 消耗积分: '1000' },
-    // { 兑换物品: '主动打招呼加量包(15次包)', 兑换时间: '2024-06-21 09:05', 消耗积分: '1500' },
-    // { 兑换物品: '职业分析报告1次体验卡', 兑换时间: '2024-06-22 10:27', 消耗积分: '600' },
-    // { 兑换物品: '星巴克美式一杯', 兑换时间: '2024-06-22 10:27', 消耗积分: '2000' },
-  ]
+const getData = async () => {
+  const res  = await getRedeemPage(queryParams.value)
+  dataList.value = res.list
+  total.value = res.total
+}
+if (getToken()) getData()
+
+const handleChangePage = (e) => {
+  queryParams.value.pageNo = e
+  getData()
 }
 }
-getData()
 </script>
 </script>
+
 <style lang="scss" scoped>
 <style lang="scss" scoped>
 </style>
 </style>

+ 37 - 13
src/views/mall/index.vue

@@ -1,20 +1,21 @@
 <!-- 门墩儿商城 -->
 <!-- 门墩儿商城 -->
  <template>
  <template>
-  <div style="background-color: var(--default-bgc); height: 100vh; overflow-y: auto;">
-    <div class="default-width white-bgc px-3 pt-5 pb-10" style="min-height: 100vh;">
+  <div style="background-color: var(--default-bgc);">
+    <div class="default-width white-bgc px-3 pt-5 pb-10">
       <div class="statisticsBox">
       <div class="statisticsBox">
         <div class="mt-2">
         <div class="mt-2">
           <span style="font-size: 20px; color: var(--color-333); line-height: 28px; font-weight: bold;" class="ml-10">您当前可用积分</span>
           <span style="font-size: 20px; color: var(--color-333); line-height: 28px; font-weight: bold;" class="ml-10">您当前可用积分</span>
-          <span @click="toPointsDetails" style="cursor: pointer;">
+          <span v-if="getToken()" @click="toPointsDetails" style="cursor: pointer;">
             <span style="font-size: 14px; color: var(--color-666); line-height: 24px;" class="ml-2">积分明细</span>
             <span style="font-size: 14px; color: var(--color-666); line-height: 24px;" class="ml-2">积分明细</span>
             <v-icon style="font-size: 16px; color: var(--color-666); line-height: 16px; margin-left: 2px;">mdi-help-circle-outline</v-icon>
             <v-icon style="font-size: 16px; color: var(--color-666); line-height: 16px; margin-left: 2px;">mdi-help-circle-outline</v-icon>
           </span>
           </span>
         </div>
         </div>
         <div class="d-flex justify-space-between align-end my-1">
         <div class="d-flex justify-space-between align-end my-1">
-          <span style="font-size: 42px; color: #10897bba; line-height: 50px;" class="ml-10 cursor-pointer" @click="toPointsDetails">{{ accountData.point }}</span>
+          <span v-if="getToken()" style="font-size: 42px; color: #10897bba; line-height: 50px;" class="ml-10 cursor-pointer" @click="toPointsDetails">{{ accountData.point }}</span>
+          <span v-else style="font-size: 30px; line-height: 50px;" class="ml-10 cursor-pointer color-primary" @click="handleLogin">请先登录</span>
         </div>
         </div>
+        <div v-if="getToken()" class="color-666 font-size-14 cursor-pointer text-end mr-5 commonHover" @click="handleToTaskCenter">赚取积分</div>
       </div>
       </div>
-      <!-- <v-tabs v-model="tab" class="mt-1" align-tabs="start" color="primary" bg-color="#fff" @update:model-value="getPositionList()"> -->
       <v-tabs v-model="tab" class="mt-1" align-tabs="start" color="primary" bg-color="#fff" @update:model-value="getPositionList">
       <v-tabs v-model="tab" class="mt-1" align-tabs="start" color="primary" bg-color="#fff" @update:model-value="getPositionList">
         <v-tab :value="1">{{ $t('points.exchange') }}</v-tab>
         <v-tab :value="1">{{ $t('points.exchange') }}</v-tab>
         <v-tab :value="2">{{ $t('points.exchangeRecords') }}</v-tab>
         <v-tab :value="2">{{ $t('points.exchangeRecords') }}</v-tab>
@@ -22,7 +23,7 @@
       <div class="mt-5">
       <div class="mt-5">
         <!-- 积分兑换 -->
         <!-- 积分兑换 -->
         <div v-if="tab === 1">
         <div v-if="tab === 1">
-          <exchange></exchange>
+          <exchange :point="accountData.point" @login="handleLogin" @toTaskCenter="handleToTaskCenter"></exchange>
         </div>
         </div>
         <!-- 积分兑换记录 -->
         <!-- 积分兑换记录 -->
         <div v-if="tab === 2">
         <div v-if="tab === 2">
@@ -31,28 +32,33 @@
       </div>
       </div>
     </div>
     </div>
   </div>
   </div>
+
+  <!-- 快速登录 -->
+  <loginPage v-if="showLogin" @loginSuccess="loginSuccess" @close="loginClose"></loginPage>
 </template>
 </template>
 
 
 <script setup>
 <script setup>
-import exchange from './exchange.vue'
-import exchangeRecords from './exchangeRecords.vue'
+defineOptions({name: 'personal-pointsMall'})
 import { ref } from 'vue'
 import { ref } from 'vue'
 import { useUserStore } from '@/store/user'
 import { useUserStore } from '@/store/user'
 import { useRoute } from 'vue-router'; const route = useRoute()
 import { useRoute } from 'vue-router'; const route = useRoute()
 import { useRouter } from 'vue-router'; const router = useRouter()
 import { useRouter } from 'vue-router'; const router = useRouter()
-defineOptions({name: 'personal-pointsMall'})
+import { getToken } from '@/utils/auth'
+import Snackbar from '@/plugins/snackbar'
+import exchangeRecords from './exchangeRecords.vue'
+import exchange from './exchange.vue'
+import loginPage from '@/views/common/loginDialog.vue'
 
 
 const toPointsDetails = () => {
 const toPointsDetails = () => {
   router.push({ path: '/recruit/personal/personalCenter/wallet' })
   router.push({ path: '/recruit/personal/personalCenter/wallet' })
 }
 }
 
 
 const getPositionList = () => {
 const getPositionList = () => {
-  if (route.query) router.replace({ path: route.path }) // 不留记录的清除跳转带过来的参数
+  if (route.query) router.replace({ path: route.path })
 }
 }
 const tab = ref(+route.query?.tab || 1)
 const tab = ref(+route.query?.tab || 1)
 
 
 const userStore = useUserStore()
 const userStore = useUserStore()
-// 'userAccount' : 'enterpriseUserAccount'
 const key = 'userAccount'
 const key = 'userAccount'
 let accountData = ref(JSON.parse(localStorage.getItem(key)) || {})
 let accountData = ref(JSON.parse(localStorage.getItem(key)) || {})
 
 
@@ -60,6 +66,26 @@ userStore.$subscribe((mutation, state) => {
   if (Object.keys(state[key]).length) accountData.value = state[key]
   if (Object.keys(state[key]).length) accountData.value = state[key]
 })
 })
 
 
+const showLogin = ref(false)
+const handleLogin = () => {
+  showLogin.value = true
+}
+
+// 快速登录
+const loginSuccess = () => {
+  showLogin.value = false
+  Snackbar.success('登录成功')
+}
+
+const loginClose = () => {
+  showLogin.value = false
+  Snackbar.warning('您已取消登录')
+}
+
+// 跳转任务中心
+const handleToTaskCenter = () => {
+  router.push('/recruit/personal/personalCenter/memberBenefits/taskCenter')
+}
 </script>
 </script>
 
 
 <style lang="scss" scoped>
 <style lang="scss" scoped>
@@ -67,7 +93,5 @@ userStore.$subscribe((mutation, state) => {
   padding: 10px 0;
   padding: 10px 0;
   border-radius: 10px;
   border-radius: 10px;
   background-color: var(--default-bgc);
   background-color: var(--default-bgc);
-  // background-color: var(--color-f3);
-  // font-family: 宋体, SimSun;
 }
 }
 </style>
 </style>

+ 9 - 7
src/views/recruit/enterprise/positionManagement/components/item.vue

@@ -5,17 +5,21 @@
       <div v-if="tab === 1" class="ml-8">
       <div v-if="tab === 1" class="ml-8">
         <v-btn :disabled="!selectAll" color="primary" variant="tonal" size="small" @click="handleAction(2, 'batch', {})">{{ $t('common.refresh') }}</v-btn>
         <v-btn :disabled="!selectAll" color="primary" variant="tonal" size="small" @click="handleAction(2, 'batch', {})">{{ $t('common.refresh') }}</v-btn>
         <v-btn class="mx-3" :disabled="!selectAll" color="primary" variant="tonal" size="small" @click="handleAction(3, 'top', {})">{{ $t('common.topping') }}</v-btn>
         <v-btn class="mx-3" :disabled="!selectAll" color="primary" variant="tonal" size="small" @click="handleAction(3, 'top', {})">{{ $t('common.topping') }}</v-btn>
+        <v-btn class="mr-3" :disabled="!selectAll" color="primary" variant="tonal" size="small" @click="handleAction(4, 'top', {})">取消置顶</v-btn>
         <v-btn :disabled="!selectAll" color="primary" variant="tonal" size="small" @click="handleAction(0, 'close', {})">{{ $t('common.close') }}</v-btn>
         <v-btn :disabled="!selectAll" color="primary" variant="tonal" size="small" @click="handleAction(0, 'close', {})">{{ $t('common.close') }}</v-btn>
       </div>
       </div>
       <v-btn v-if="tab === 2" class="ml-8" :disabled="!selectAll" color="primary" variant="tonal" size="small" @click="handleAction(1, 'activation', {})">{{ $t('common.activation') }}</v-btn>
       <v-btn v-if="tab === 2" class="ml-8" :disabled="!selectAll" color="primary" variant="tonal" size="small" @click="handleAction(1, 'activation', {})">{{ $t('common.activation') }}</v-btn>
     </div>
     </div>
     <div v-for="val in items" :key="val.id" class="itemBox mb-3" style="height: 134px;">
     <div v-for="val in items" :key="val.id" class="itemBox mb-3" style="height: 134px;">
+      <div v-if="val.top && tab === 1" style="position: absolute;">
+        <svg-icon name="top" size="50"></svg-icon>
+      </div>
       <div class="d-flex justify-space-between" style="padding: 10px 20px;">
       <div class="d-flex justify-space-between" style="padding: 10px 20px;">
         <div class="position">
         <div class="position">
-          <div class="item-select" v-if="tab !== 3">
+          <div class="item-select ml-5" v-if="tab !== 3">
             <v-checkbox v-model="val.select" hide-details color="primary" @update:model-value="handleChangeSelect"></v-checkbox>
             <v-checkbox v-model="val.select" hide-details color="primary" @update:model-value="handleChangeSelect"></v-checkbox>
           </div>
           </div>
-          <div class="d-flex align-center" :class="{'cursor-pointer': tab === 1, 'ml-10': tab !== 3}" @click="handleEdit(val)">
+          <div class="d-flex align-center" :class="{'cursor-pointer': tab === 1, 'ml-15': tab !== 3}" @click="handleEdit(val)">
             <span v-if="val.name.indexOf('style')" v-html="val.name" class="position-name"></span>
             <span v-if="val.name.indexOf('style')" v-html="val.name" class="position-name"></span>
             <span v-else class="position-name">{{ val.name }}</span>
             <span v-else class="position-name">{{ val.name }}</span>
           </div>
           </div>
@@ -52,7 +56,7 @@
         <div class="d-flex">
         <div class="d-flex">
           <div class="ml-10 d-flex">
           <div class="ml-10 d-flex">
             <div v-if="tab === 1">
             <div v-if="tab === 1">
-              <span class="cursor-pointer actions" @click="handleAction(3, '', val)">{{ $t('common.topping') }}</span>
+              <span class="cursor-pointer actions" @click="handleAction(val.top ? 4 : 3, '', val)">{{ val.top ? '取消置顶' : $t('common.topping') }}</span>
               <span class="lines"></span>
               <span class="lines"></span>
               <span class="cursor-pointer actions" @click="handleAction(0, '', val)">{{ $t('common.close') }}</span>
               <span class="cursor-pointer actions" @click="handleAction(0, '', val)">{{ $t('common.close') }}</span>
               <span class="lines"></span>
               <span class="lines"></span>
@@ -78,7 +82,7 @@ import { ref, watch } from 'vue'
 import { useRouter } from 'vue-router'
 import { useRouter } from 'vue-router'
 import { timesTampChange } from '@/utils/date'
 import { timesTampChange } from '@/utils/date'
 import { useI18n } from '@/hooks/web/useI18n'
 import { useI18n } from '@/hooks/web/useI18n'
-import { closeJobAdvertised, enableJobAdvertised, refreshJobAdvertised, topJobAdvertised, updatePositionExpireTime } from '@/api/position'
+import { closeJobAdvertised, enableJobAdvertised, refreshJobAdvertised, topJobAdvertised, updatePositionExpireTime, topJobAdvertisedCancel } from '@/api/position'
 import Snackbar from '@/plugins/snackbar'
 import Snackbar from '@/plugins/snackbar'
 
 
 const { t } = useI18n()
 const { t } = useI18n()
@@ -152,7 +156,7 @@ watch(
   { deep: true }
   { deep: true }
 )
 )
 
 
-const apiList = [ closeJobAdvertised, enableJobAdvertised, refreshJobAdvertised, topJobAdvertised ]
+const apiList = [closeJobAdvertised, enableJobAdvertised, refreshJobAdvertised, topJobAdvertised, topJobAdvertisedCancel]
 
 
 // 职位关闭、激活、刷新、置顶
 // 职位关闭、激活、刷新、置顶
 const handleAction = async (index, type, { id }) => {
 const handleAction = async (index, type, { id }) => {
@@ -178,8 +182,6 @@ const handleUpdateExpireTime = (item) => {
   showExpire.value = true
   showExpire.value = true
 }
 }
 const handleSubmit = async () => {
 const handleSubmit = async () => {
-  // const { valid } = await CtFormRef.value.formRef.validate()
-  // if (!valid) return
   const time = formItem.value.options.find(e => e.key === 'time').value
   const time = formItem.value.options.find(e => e.key === 'time').value
   if (!time) return Snackbar.warning('请选择职位到期时间')
   if (!time) return Snackbar.warning('请选择职位到期时间')
   await updatePositionExpireTime({ id: expireTimeId.value, time })
   await updatePositionExpireTime({ id: expireTimeId.value, time })

+ 4 - 4
src/views/recruit/personal/PersonalCenter/resume/online/components/educationExp.vue

@@ -170,11 +170,11 @@ const formItems = ref({
       key: 'startTime',
       key: 'startTime',
       mode: 'month', // 时间类型 year month date time
       mode: 'month', // 时间类型 year month date time
       value: null,
       value: null,
-      default: '2000-01',
+      default: '2014-01',
       format: 'YYYY/MM',
       format: 'YYYY/MM',
       labelWidth: 120,
       labelWidth: 120,
       label: '开始时间 *',
       label: '开始时间 *',
-      defaultValue: new Date(2000, 1),
+      defaultValue: new Date(2014, 1),
       disabledFutureDates: true,
       disabledFutureDates: true,
       col: 6,
       col: 6,
       rules: [v => !!v || '请选择起始时间']
       rules: [v => !!v || '请选择起始时间']
@@ -184,9 +184,9 @@ const formItems = ref({
       key: 'endTime',
       key: 'endTime',
       mode: 'month', // 时间类型 year month date time
       mode: 'month', // 时间类型 year month date time
       value: null,
       value: null,
-      default: '2004-01',
+      default: '2018-01',
       format: 'YYYY/MM',
       format: 'YYYY/MM',
-      defaultValue: new Date(2004, 1),
+      defaultValue: new Date(2018, 1),
       disabledFutureDates: true,
       disabledFutureDates: true,
       labelWidth: 120,
       labelWidth: 120,
       label: '结束时间 *',
       label: '结束时间 *',

+ 46 - 0
src/views/recruit/personal/PersonalCenter/resume/online/components/portrait.vue

@@ -5,6 +5,12 @@
     <v-btn icon="mdi-plus" variant="outlined" color="primary" size="small" @click="handleAdd"></v-btn>
     <v-btn icon="mdi-plus" variant="outlined" color="primary" size="small" @click="handleAdd"></v-btn>
   </div>
   </div>
 
 
+  <div class="d-flex align-center cursor-pointer color-primary mt-3 font-size-18" @click="handleShareCode">
+    <v-icon class="mr-1">mdi-qrcode</v-icon>我的分享码
+    <span class="text-decoration-underline">(点击生成)</span>
+  </div>
+
+  <!-- 个人画像标签编辑 -->
   <CtDialog
   <CtDialog
     :visible="visible"
     :visible="visible"
     titleClass="text-h6"
     titleClass="text-h6"
@@ -43,6 +49,22 @@
     </div>
     </div>
   </CtDialog>
   </CtDialog>
 
 
+  <!-- 分享二维码生成 -->
+  <CtDialog
+    :visible="showShare"
+    titleClass="text-h6"
+    title="我的分享码"
+    :widthType="2"
+    @close="showShare = false; shareUrl = ''"
+    submitText="保存到本地"
+    @submit="handleShareSubmit"
+  >
+    <div class="d-flex align-center justify-center flex-column">
+      <p class="color-primary font-size-18 mb-3">邀请用户注册领50积分</p>
+      <img :src="shareUrl" width="200" height="200">
+    </div>
+  </CtDialog>
+
   <Loading :visible="loading"></Loading>
   <Loading :visible="loading"></Loading>
 </template>
 </template>
 
 
@@ -52,9 +74,11 @@ defineOptions({ name: 'person-portrait'})
 import { ref } from 'vue'
 import { ref } from 'vue'
 import { getTagTreeDataApi } from '@/api/enterprise'
 import { getTagTreeDataApi } from '@/api/enterprise'
 import { useUserStore } from '@/store/user'
 import { useUserStore } from '@/store/user'
+import { getJobAdvertisedShareQrcode } from '@/api/position'
 import { savePersonPortrait } from '@/api/recruit/personal/resume'
 import { savePersonPortrait } from '@/api/recruit/personal/resume'
 import Snackbar from '@/plugins/snackbar'
 import Snackbar from '@/plugins/snackbar'
 import cloneDeep from 'lodash/cloneDeep'
 import cloneDeep from 'lodash/cloneDeep'
+import { downloadBase64 } from '@/utils'
 
 
 const loading = ref(false)
 const loading = ref(false)
 const visible = ref(false)
 const visible = ref(false)
@@ -123,6 +147,28 @@ const handleSubmit = async () => {
     visible.value = false
     visible.value = false
   }
   }
 }
 }
+
+// 生成分享二维码
+const showShare = ref(false)
+const shareUrl = ref(null)
+const handleShareCode = async () => {
+  const query = {
+    scene: 'shareId=' + JSON.parse(localStorage.getItem('accountInfo')).userId,
+    path: 'pages/login/index',
+    width: 200,
+    autoColor: false,
+    checkPath: true,
+    hyaline: true
+  }
+  const data = await getJobAdvertisedShareQrcode(query)
+  shareUrl.value = 'data:image/png;base64,' + data
+  showShare.value = true
+}
+
+// 保存到本地
+const handleShareSubmit = () => {
+  downloadBase64(shareUrl.value, '我的分享码')
+}
 </script>
 </script>
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">

+ 83 - 0
src/views/recruit/personal/PersonalCenter/tradeOrder/index.vue

@@ -0,0 +1,83 @@
+<!--  -->
+<template>
+  <div class="white-bgc pa-3 ma-3">
+    <!-- <v-tabs v-model="tab" align-tabs="start" color="primary" bg-color="#f7f8fa" @update:model-value="handleChangeTab">
+      <v-tab :value="0">{{ '交易订单' }}</v-tab>
+    </v-tabs> -->
+    <CtTable
+      class="mt-3"
+      :items="dataList"
+      :headers="headers"
+      :loading="false"
+      :elevation="0"
+      :isTools="false"
+      :showPage="true"
+      :total="total"
+      :page-info="query"
+      itemKey="id"
+      @pageHandleChange="handleChangePage"
+    >
+    </CtTable>
+  </div>
+</template>
+
+<script setup>
+defineOptions({name: 'tradingOrder'})
+import { getUserTradeOrder } from '@/api/recruit/personal/personalCenter'
+import { ref } from 'vue'
+import { timesTampChange } from '@/utils/date'
+import { getDict } from '@/hooks/web/useDictionaries'
+
+// const tab = ref(0)
+const total = ref(0)
+const query = ref({
+  pageNo: 1,
+  pageSize: 10
+})
+const dataList = ref([])
+
+// 支付渠道
+const channelData = ref([])
+const getPayChannelCode = async () => {
+  const { data } = await getDict('pay_channel_code')
+  channelData.value = data
+}
+getPayChannelCode()
+
+
+const headers = [
+  { title: '商品名称', key: 'spuName', sortable: false },
+  { title: '价格', key: 'price', sortable: false, value: item => item.price + '元' },
+  { title: '是否已支付', key: 'payStatus', sortable: false, value: item => item.payStatus ? '已支付' : '未支付' },
+  { title: '支付订单编号', key: 'payOrderId', sortable: false },
+  { title: '支付渠道', key: 'payChannelCode', value: item => channelData.value.find(e => e.value === item.payChannelCode)?.label, sortable: false },
+  { title: '订单支付时间', key: 'payTime', value: item =>  timesTampChange(item.payTime), sortable: false },
+  { title: '退款订单编号', key: 'payReFundId', sortable: false },
+  { title: '退款金额', key: 'refundPrice', sortable: false },
+  { title: '退款时间', key: 'refundTime', value: item =>  timesTampChange(item.refundTime), sortable: false },
+  { title: '更新时间', key: 'updateTime', value: item =>  timesTampChange(item.updateTime), sortable: false },
+]
+
+const getData = async () => {
+  const res = await getUserTradeOrder(query.value)
+  dataList.value = res.list
+  total.value = res.total
+}
+getData()
+
+const handleChangePage = (e) => {
+  query.value.pageNo = e
+  getData()
+}
+
+// 切换
+// const handleChangeTab = () => {
+//   query.value.pageNo = 1
+//   query.value.type = tab.value
+//   getData()
+// }
+
+// const headers = ref([orderHeaders.value])
+</script>
+<style lang="scss" scoped>
+</style>

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