Sfoglia il codice sorgente

feat: 更新离线活动项组件,添加图片宽度适配模式;更新任务相关请求,添加待办分页接口;优化数据表单和模型,支持更多字段

EvilDragon 3 mesi fa
parent
commit
9bec0ee39b

+ 46 - 12
packages/app/src/core/libs/models.ts

@@ -741,30 +741,64 @@ export interface Designer {
   focus: boolean
   brokerId: number
 }
+export interface Broker {
+  createTime: string
+  updateTime: string
+  creator: string
+  updater: string
+  deleted: boolean
+  id: number
+  brokeId: string
+  inviteCode: string
+  inviteCodeUrl: string
+  headImgUrl: string
+  brokerName: string
+  password: string
+  mobile: string
+  channelType: number
+  status: number
+  points: number
+}
+export interface Report {
+  createTime: string
+  updateTime: string
+  creator: string
+  updater: string
+  deleted: boolean
+  id: number
+  verifyTime: string
+  verifier: number
+  status: string
+  taskId: number
+  brokerId: number
+  reason: string
+  remark: string
+  num: number
+}
 export interface AgentTask {
   id: number
   bearerId: number
   bearerName: string
-  bearerType: number
+  bearerType: string
   name: string
   storeQuantity: number
   completedNum: number
   personalCompletedNum: number
-  startTime: number
-  endTime: number
+  startTime: string
+  endTime: string
   pointsReward: number
   shopIds: string
-  shopNames?: string
+  shopNames: string
   detail: string
-  taskType: number
-  finalType: number
-  roleType: number
-  status: number
-  createTime: number
+  taskType: string
+  finalType: string
+  roleType: string
+  status: string
+  createTime: string
   receive: boolean
-  brokerIds?: string
-  brokerList: any
-  reportList: any
+  brokerIds: string
+  brokerList: Broker[]
+  reportList: Report[]
   bearerTypeName: string
   taskTypeName: string
   finalTypeName: string

+ 1 - 0
packages/app/src/pages/home/components/offline-activity-item.vue

@@ -26,6 +26,7 @@ const router = useRouter()
           width="100%"
           custom-class="vertical-bottom"
           class="w-[347px] h-[202px] rounded-tl-2xl rounded-tr-2xl"
+          mode="widthFix"
           :src="options.bannerUrl"
         />
         <template v-if="dayjs().isAfter(dayjs(options?.hostDate).add(1, 'd'))">

+ 6 - 4
packages/app/src/pages/publish/moment/index.vue

@@ -88,10 +88,12 @@ const handleChange = ({ fileList: files }) => {
   console.log(fileList.value)
 }
 const handleSubmit = async () => {
-  const { valid, errors } = await dataFormRef.value.validate()
-  console.log(valid, errors)
-  if (!valid) {
-    return false
+  if (circleType.value === CircleType.case) {
+    const { valid, errors } = await dataFormRef.value.validate()
+    console.log(valid, errors)
+    if (!valid) {
+      return false
+    }
   }
   if (!fileList.value.length) {
     toast(messages.moment.imageNotExist)

+ 10 - 3
packages/merchant/src/components/data-form.ts

@@ -1,12 +1,19 @@
 export interface DataFormProps {
-  labelWidth?: number
   defaultValue?: Date
+  placeholder?: string
+  columns?: { label: string; value: string }[]
+  disabled?: boolean
+  'onUpdate:modelValue'?: (value: string) => void
+  type?: 'nickname'
 }
 export interface DataFormSchema {
   [key: symbol | string]: {
-    type: 'TextField' | 'Select' | 'Radio' | 'Submit' | 'TimePick' | 'Textarea'
+    type: 'TextField' | 'Select' | 'Radio' | 'Submit' | 'TimePick' | 'Textarea' | 'Checkbox'
     label?: string
-    existing?: string
+    labelWidth?: number
+    hiddenLabel?: boolean
+    existing?: boolean
+    required?: boolean
     props?: DataFormProps
   }
 }

+ 55 - 49
packages/merchant/src/components/data-form.vue

@@ -3,8 +3,10 @@ import WdButton from 'wot-design-uni/components/wd-button/wd-button.vue'
 import WdInput from 'wot-design-uni/components/wd-input/wd-input.vue'
 import WdPicker from 'wot-design-uni/components/wd-picker/wd-picker.vue'
 import { ConfigProviderThemeVars } from 'wot-design-uni'
-import { DataFormProps } from './data-form'
+import { DataFormProps, DataFormSchema } from './data-form'
 import { addUnit } from 'wot-design-uni/components/common/util'
+import { omit } from 'radash'
+import WdForm from 'wot-design-uni/components/wd-form/wd-form.vue'
 
 const modelValue = defineModel({
   type: Object,
@@ -12,20 +14,14 @@ const modelValue = defineModel({
 })
 withDefaults(
   defineProps<{
-    schema: {
-      [key: symbol]: {
-        type: 'TextField' | 'Select' | 'Radio' | 'Submit' | 'timePick' | 'TimePick' | 'textarea'
-        label?: string
-        existing?: boolean
-        props?: DataFormProps
-      }
-    }
+    schema: DataFormSchema
+    rules?: { [key: string]: { required: boolean; message: string }[] }
     direction?: 'horizontal' | 'vertical'
   }>(),
-  { direction: 'vertical' },
+  { direction: 'vertical', labelShow: true },
 )
 const emits = defineEmits(['submit'])
-const form = ref()
+const form = ref<InstanceType<typeof WdForm>>()
 const types = {
   TextField: WdInput,
   Submit: WdButton,
@@ -67,6 +63,9 @@ const horizontalDefaultProps = {
   Radio: {
     customClass: 'my--4!',
   },
+  Checkbox: {
+    customClass: 'my--4!',
+  },
   TimePick: {
     customClass: 'm-0!',
   },
@@ -76,20 +75,27 @@ const themeVars: ConfigProviderThemeVars = {
   cellWrapperPadding: '10rpx',
   radioButtonRadius: '8rpx',
   radioButtonBg: 'transparent',
+  checkboxButtonRadius: '8rpx',
+  checkboxButtonBg: 'transparent',
 }
 const submit = () => {
   emits('submit', modelValue)
 }
-const validate = (): Promise<{ valid: boolean; errors: any[] }> => form.value?.validate()
+const validate = async (): Promise<{ valid: boolean; errors: any[] }> => {
+  return await form.value.validate()
+}
 defineExpose({
   validate,
 })
 </script>
 <template>
   <wd-config-provider :theme-vars="themeVars">
-    <wd-form ref="form" :model="modelValue">
+    <wd-form ref="form" error-type="toast" :rules="rules" :model="modelValue">
+      <!-- <wd-cell-group border> -->
       <template
-        v-for="([prop, { type, label, existing, props }], index) in Object.entries(schema)"
+        v-for="(
+          [prop, { type, label, labelWidth, hiddenLabel, existing, required, props }], index
+        ) in Object.entries(schema)"
         :key="index"
       >
         <div
@@ -98,44 +104,36 @@ defineExpose({
           :class="[direction === 'horizontal' ? 'items-start' : '']"
           :style="
             direction === 'horizontal'
-              ? { 'grid-template-columns': `${addUnit(props?.labelWidth)} auto` }
+              ? { 'grid-template-columns': `${addUnit(labelWidth || 100)} auto` }
               : {}
           "
         >
-          <div class="flex items-center h-11">
-            <label
-              v-if="type !== 'Submit'"
-              class="text-sm font-normal leading-relaxed"
-              :class="[direction === 'horizontal' ? 'text-black/60' : 'mb-1 text-black/40']"
-              :for="prop"
+          <label
+            v-if="type !== 'Submit' && !hiddenLabel"
+            class="text-sm font-normal leading-relaxed"
+            :class="[direction === 'horizontal' ? 'text-black/60' : 'mb-1 text-black/40']"
+            :for="prop"
+          >
+            <span
+              v-if="required"
+              class="text-[#ef4343] text-base font-normal font-['PingFang_SC'] leading-normal"
             >
-              <span v-if="props?.required" style="color: #ff2e2e">*</span>
-              {{ label || prop }}
-            </label>
-          </div>
+              *
+            </span>
+            {{ label || prop }}
+          </label>
           <wd-input
             v-if="type === 'TextField'"
             v-bind="{
               ...(direction === 'vertical'
                 ? verticalDefaultProps[type]
                 : horizontalDefaultProps[type]),
-              ...props,
+              ...omit(props, []),
             }"
             v-model="modelValue[prop]"
           ></wd-input>
           <wd-datetime-picker
             v-model="modelValue[prop]"
-            v-if="type === 'timePick'"
-            v-bind="{
-              ...(direction === 'vertical'
-                ? verticalDefaultProps[type]
-                : horizontalDefaultProps[type]),
-              cell: false,
-              ...props,
-            }"
-          />
-          <wd-datetime-picker
-            v-model="modelValue[prop]"
             v-if="type === 'TimePick'"
             v-bind="{
               ...(direction === 'vertical'
@@ -146,17 +144,6 @@ defineExpose({
             }"
           />
           <wd-textarea
-            v-if="type === 'textarea'"
-            v-model="modelValue[prop]"
-            v-bind="{
-              ...(direction === 'vertical'
-                ? verticalDefaultProps[type]
-                : horizontalDefaultProps[type]),
-              cell: false,
-              ...props,
-            }"
-          />
-          <wd-textarea
             v-if="type === 'Textarea'"
             v-model="modelValue[prop]"
             v-bind="{
@@ -178,6 +165,7 @@ defineExpose({
             }"
             v-model="modelValue[prop]"
           ></wd-picker>
+
           <wd-radio-group
             v-if="type === 'Radio'"
             v-bind="{
@@ -194,11 +182,28 @@ defineExpose({
               <wd-radio :value="value">{{ label }}</wd-radio>
             </template>
           </wd-radio-group>
+
+          <wd-checkbox-group
+            v-if="type === 'Checkbox'"
+            v-bind="{
+              ...(direction === 'vertical'
+                ? verticalDefaultProps[type]
+                : horizontalDefaultProps[type]),
+              ...props,
+              cell: true,
+              shape: 'button',
+            }"
+            v-model="modelValue[prop]"
+          >
+            <template v-for="{ label, value } of props.columns" :key="value">
+              <wd-checkbox custom-class="mr-4!" :modelValue="value">{{ label }}</wd-checkbox>
+            </template>
+          </wd-checkbox-group>
           <wd-button
             v-if="type === 'Submit'"
             v-bind="{
               ...(direction === 'vertical' ? verticalDefaultProps[type] : {}),
-              ...props,
+              ...omit(props, []),
               formType: 'submit',
             }"
             @click="submit"
@@ -207,6 +212,7 @@ defineExpose({
           </wd-button>
         </div>
       </template>
+      <!-- </wd-cell-group> -->
     </wd-form>
   </wd-config-provider>
 </template>

+ 14 - 2
packages/merchant/src/core/libs/requests.ts

@@ -2,7 +2,13 @@ import { httpGet, httpPost, httpPut, httpDelete } from '../../utils/http'
 import { Schedule } from '../models/schedule'
 import { Moment } from '../models/moment'
 import dayjs from 'dayjs'
-import { PointsOrder, ResPageData, Todo, Agent } from '@designer-hub/app/src/core/libs/models'
+import {
+  PointsOrder,
+  ResPageData,
+  Todo,
+  Agent,
+  AgentTask,
+} from '@designer-hub/app/src/core/libs/models'
 import { get } from 'radash'
 
 export const getUserInfo = () =>
@@ -228,7 +234,8 @@ export const createReportInfo = (data) => httpPost<any>('/app-api/member/report-
 // 获取任务列表
 export const getTaskList = (query) => httpGet<any>('/app-api/member/task/task-list', query)
 // 任务详情
-export const getTaskDetail = (query) => httpGet<any>('/app-api/member/task/task-detail', query)
+export const getTaskDetail = (query) =>
+  httpGet<AgentTask>('/app-api/member/task/task-detail', query)
 // 领取任务
 export const taskReceive = (data) => httpPost<any>('/app-api/member/task/task-receive', data)
 // 任务上报
@@ -331,6 +338,11 @@ export const getPointsOrder = (id) =>
 export const getTodos = (query: { brokerId: string; executionTime: string }) =>
   httpGet<Todo[]>('/app-api/member/todo/list', query)
 /**
+ * 渠道端-获取待办分页
+ */
+export const getTodoPage = (query: { pageNo: number; pageSize: number }) =>
+  httpGet<ResPageData<Todo>>('/app-api/member/todo/page', query)
+/**
  * 渠道端-创建待办
  */
 export const createTodo = (data: Partial<Todo>) => httpPost('/app-api/member/todo/create', data)

+ 133 - 0
packages/merchant/src/pages/home/agent/components/task-card.vue

@@ -0,0 +1,133 @@
+<script setup lang="ts">
+import { storeToRefs } from 'pinia'
+import { taskReceive } from '../../../../core/libs/requests'
+import { useUserStore } from '../../../../store'
+import Card from '@designer-hub/app/src/components/card.vue'
+import dayjs from 'dayjs'
+const props = withDefaults(defineProps<{ options: any }>(), { options: () => ({}) })
+const userStore = useUserStore()
+const { userInfo } = storeToRefs(userStore)
+const types = ref({
+  // 0: { title: '团队任务', bg: '', bgClass: 'bg-gradient-to-r from-[#cfe0ff] to-[#e1ecff]' },
+  1: { title: '团队任务', bg: '', bgClass: 'bg-gradient-to-r from-[#cfe0ff] to-[#e1ecff]' },
+  2: { title: '抢单任务', bg: '', bgClass: 'bg-gradient-to-r from-[#ffcfcf] to-[#ffeae1]' },
+  3: { title: '指定任务', bg: '', bgClass: 'bg-gradient-to-r from-[#ffe8cf] to-[#fff3e1]' },
+})
+const status = ref({
+  1: {
+    title: '未开始',
+    bg: 'bg-[#f04c47]',
+    bgClass: 'bg-[#f04c47]',
+  },
+  2: { title: '进行中', bg: '', bgClass: 'bg-[#2357e9]' },
+  3: { title: '已撤回', bg: '', bgClass: 'bg-[#f04c47]' },
+  4: { title: '已完成', bg: '', bgClass: 'bg-[#f04c47]' },
+  5: { title: '未完成', bg: '', bgClass: 'bg-[#abacaf]' },
+  6: { title: '待确认', bg: '', bgClass: 'bg-[#f04c47]' },
+})
+const acceptingOrders = async (item) => {
+  uni.showLoading()
+  const res = await taskReceive({ brokerId: userInfo.value.userId, taskId: item.id, orders: true })
+  uni.hideLoading()
+  //   initData()
+}
+const acceptingNoOrders = async (item) => {
+  uni.showLoading()
+  const res = await taskReceive({ brokerId: userInfo.value.userId, taskId: item.id, orders: false })
+  uni.hideLoading()
+  //   initData()
+}
+const toDetail = () =>
+  uni.navigateTo({ url: `/pages/home/tasks/detail/index?taskId=${props.options.id}` })
+</script>
+<template>
+  <Card
+    :custom-class="`${types[options.taskType].bgClass} p-0`"
+    style="padding: 0"
+    @click="toDetail"
+  >
+    <div class="flex p-4 items-center">
+      <div
+        :class="`${status[options.status].bgClass} w-[47px] h-[23px] px-1 rounded border justify-center items-center gap-2.5 inline-flex`"
+      >
+        <div class="text-right text-white text-xs font-normal font-['PingFang_SC'] leading-tight">
+          {{ status[options.status].title }}
+        </div>
+      </div>
+      <div class="mx-2.5 text-black/90 text-lg font-normal font-['PingFang_SC'] leading-none">
+        {{ types[options.taskType].title }}
+      </div>
+      <div class="flex-1"></div>
+      <div class="mx-1.5 text-black/40 text-xs font-normal font-['PingFang_SC'] leading-none">
+        奖励积分
+      </div>
+      <div class="text-[#ff2e2e] text-[22px] font-medium font-['DIN'] leading-none">
+        {{ options.pointsReward }}
+      </div>
+    </div>
+    <div class="flex flex-col gap-4 bg-white p-5 rounded-2xl">
+      <div>
+        <span class="text-black/40 text-sm font-normal font-['PingFang_SC'] leading-none">
+          任务名称:
+        </span>
+        <span class="text-black/60 text-sm font-normal font-['PingFang_SC'] leading-none">
+          {{ options.name }}
+        </span>
+      </div>
+      <div>
+        <span class="text-black/40 text-sm font-normal font-['PingFang_SC'] leading-none">
+          发起方:
+        </span>
+        <span class="text-black/60 text-sm font-normal font-['PingFang_SC'] leading-none">
+          {{ options.bearerName }}
+        </span>
+      </div>
+
+      <div>
+        <span class="text-black/40 text-sm font-normal font-['PingFang_SC'] leading-none">
+          任务时间:
+        </span>
+        <span class="text-black/60 text-sm font-normal font-['PingFang_SC'] leading-none">
+          {{ dayjs(options.startTime).format('YYYY/MM/DD') }}-{{
+            dayjs(options.endTime).format('YYYY/MM/DD')
+          }}
+        </span>
+      </div>
+      <div class="flex items-center border-t border-t-solid border-t-[#efefef] pt-1.5">
+        <div class="text-black/90 text-sm font-normal font-['PingFang SC']">
+          目标 {{ options.storeQuantity }}
+        </div>
+        <template v-if="!options.receive">
+          <div class="flex-1"></div>
+          <div class="text-black/90 text-sm font-normal font-['PingFang SC']">
+            个人完成
+            <span style="color: #2357e9">{{ options.personalCompletedNum }}</span>
+          </div>
+          <div class="flex-1"></div>
+          <div class="text-black/90 text-sm font-normal font-['PingFang SC']">
+            累计完成
+            <span style="color: #f1981b">{{ options.completedNum }}</span>
+          </div>
+        </template>
+        <template v-else>
+          <div class="flex-1"></div>
+          <div
+            v-if="options.receive"
+            class="mr-[16px] w-[68px] h-7 px-2.5 py-[3px] rounded-[30px] border border-[#fe5053] justify-center items-center gap-2.5 inline-flex"
+            style="border: 1px solid #fe5053"
+            @click.stop="acceptingNoOrders(options)"
+          >
+            <div class="w-9 text-[#ff2d2d] text-xs font-normal font-['PingFang SC']">不接单</div>
+          </div>
+          <div
+            v-if="options.receive"
+            @click.stop="acceptingOrders(options)"
+            class="w-[68px] h-7 px-2.5 py-[3px] bg-[#2357e9] rounded-[30px] justify-center items-center gap-2.5 inline-flex"
+          >
+            <div class="text-white text-xs font-normal font-['PingFang SC']">接单</div>
+          </div>
+        </template>
+      </div>
+    </div>
+  </Card>
+</template>

+ 33 - 4
packages/merchant/src/pages/home/index.vue

@@ -11,7 +11,13 @@
 
 <script lang="ts" setup>
 import dayjs from 'dayjs'
-import { getDeignerPointsActivities, scanCodeCheckPaper } from '../../core/libs/requests'
+import {
+  getDeignerPointsActivities,
+  getTaskList,
+  getTodoPage,
+  getTodos,
+  scanCodeCheckPaper,
+} from '../../core/libs/requests'
 import SectionHeading from '@designer-hub/app/src/components/section-heading.vue'
 import Card from '@designer-hub/app/src/components/card.vue'
 import { merchantPageHeaderBg, scanIcon, bookIcon } from '@designer-hub/assets/src/svgs'
@@ -21,6 +27,8 @@ import { qrCodeString2Object, requestToast } from '@designer-hub/app/src/core/ut
 import { QrCodeBusinessType } from '../../core/libs/enums'
 import { useRouter } from '../../composables/router'
 import PageHelperEvo from '@/components/page-helper-evo.vue'
+import TaskCard from './agent/components/task-card.vue'
+import { getTasks } from '../../core/libs/agent-requests'
 
 defineOptions({
   name: 'Home',
@@ -30,6 +38,17 @@ const userStore = useUserStore()
 const { isLogined, userInfo, isAgent, isMerchant } = storeToRefs(userStore)
 const orderAmount = ref()
 const pointsAmount = computed(() => orderAmount.value * 10)
+const todosQuery = computed(() => ({
+  brokerId: userInfo.value.userId.toString(),
+  executionTime: [
+    dayjs().startOf('days').format('YYYY-MM-DD HH:mm:ss'),
+    dayjs().endOf('days').format('YYYY-MM-DD HH:mm:ss'),
+  ].join(','),
+}))
+const { data: tasks, run: setTasks } = useRequest(
+  () => getTasks({ brokerId: userInfo.value.userId }),
+  { initialData: { list: [], total: 0 } },
+)
 const toDesigner = () => {
   uni.navigateTo({ url: '/pages/designer/index' })
 }
@@ -60,7 +79,7 @@ const toAddReporting = () => {
   uni.navigateTo({ url: '/pages/home/merchant/add-reporting-information' })
 }
 onShow(async () => {
-  await Promise.all([])
+  isAgent.value && (await Promise.all([setTasks()]))
 })
 onLoad(() => {
   console.log(isLogined.value)
@@ -104,6 +123,7 @@ onShareAppMessage(() => ({}))
                 <wd-step description="登录账号并绑定手机" />
                 <wd-step description="完善个人信息" />
               </wd-steps> -->
+              <PageHelperEvo :request="getTodoPage" :query="todosQuery"></PageHelperEvo>
               <div
                 class="flex items-center justify-center b-t b-t-solid b-t-[#f6f6f6] pt-3.5"
                 @click="router.push('/pages/home/agent/todo/index')"
@@ -118,12 +138,21 @@ onShareAppMessage(() => ({}))
         </div>
         <div>
           <SectionHeading
-            title="渠道任务"
+            title="任务"
             path="/pages/home/tasks/index"
             end-text="查看全部"
             custom-class="mb-5"
           ></SectionHeading>
-          <Card><wd-status-tip image="search" tip="当前暂无未开始/进行中的任务" /></Card>
+          <div class="flex overflow-x-auto whitespace-nowrap mx--4 px-4 gap-4 box-border">
+            <Card v-if="!tasks.list.length">
+              <wd-status-tip image="search" tip="当前暂无未开始/进行中的任务" />
+            </Card>
+            <template v-for="(it, i) in tasks.list" :key="i">
+              <div class="inline-block">
+                <div class="w-[calc(100vw-64rpx)]"><TaskCard :options="it"></TaskCard></div>
+              </div>
+            </template>
+          </div>
         </div>
         <div>
           <SectionHeading title="设计师" path="" custom-class="mb-5"></SectionHeading>

+ 56 - 24
packages/merchant/src/pages/home/tasks/detail/index.vue

@@ -8,11 +8,12 @@ import Card from '@designer-hub/app/src/components/card.vue'
 import SectionHeading from '@designer-hub/app/src/components/section-heading.vue'
 import BottomAppBar from '@/components/bottom-app-bar.vue'
 import DataForm from '@/components/data-form.vue'
-// eslint-disable-next-line import/named
-import { appTaskReport, getTaskDetail } from '@/core/libs/requests'
-import { useUserStore } from '@/store'
+import { appTaskReport, getTaskDetail } from '../../../../core/libs/requests'
+import { useUserStore } from '../../../../store'
 import { storeToRefs } from 'pinia'
 import dayjs from 'dayjs'
+import { DataFormSchema } from '../../../../components/data-form'
+import { ComponentExposed } from 'vue-component-type-helpers'
 const taskId = ref()
 const types = ref({
   1: { title: '到店', bg: '', bgClass: 'bg-gradient-to-r from-[#cfe0ff] to-[#e1ecff]' },
@@ -24,32 +25,42 @@ const publishState = ref(false)
 const formData = ref<any>({})
 const userStore = useUserStore()
 const { userInfo } = storeToRefs(userStore)
-const taskDetails = ref()
-const customerSchema = ref({
+const { data: taskDetails, run: setTaskDetails } = useRequest(() =>
+  getTaskDetail({ brokerId: userInfo.value.userId, taskId: taskId.value }),
+)
+const dataFormRef = ref<ComponentExposed<typeof DataForm>>()
+const customerSchema = ref<DataFormSchema>({
   num: {
     type: 'TextField',
     label: '个人完成量:',
+    required: true,
     props: {
-      labelWidth: '126rpx',
       placeholder: '请输入个人完成量',
     },
   },
   remark: {
-    type: 'textarea',
+    type: 'Textarea',
     label: '上报说明:',
+    required: true,
     props: {
-      labelWidth: '126rpx',
       placeholder: '请输入上报说明',
     },
   },
 })
+const customerRules = ref({
+  num: [{ required: true, message: '请输入个人完成量', trigger: 'blur' }],
+  remark: [{ required: true, message: '请输入上报说明', trigger: 'blur' }],
+})
 const initData = async () => {
-  const res = await getTaskDetail({ brokerId: userInfo.value.userId, taskId: taskId.value })
-  taskDetails.value = res.data
+  await setTaskDetails()
 }
 const submitTask = async (data) => {
   uni.showLoading()
-
+  const { valid } = await dataFormRef.value.validate()
+  if (!valid) {
+    uni.hideLoading()
+    return
+  }
   console.log(formData.value)
   const res = await appTaskReport({
     brokerId: userInfo.value.userId,
@@ -92,15 +103,31 @@ onLoad(async (query: { taskId: string }) => {
       <div class="bg-[#f6f7ff]" style="border-top-left-radius: 8px; border-top-right-radius: 8px">
         <div class="flex flex-row items-center justify-start p-[15px] gap-2">
           <div style="width: 50px; height: 50px">
-            <wd-circle v-model="current" :size="50" color="rgba(66, 113, 255, 1)"></wd-circle>
+            <wd-circle
+              :model-value="(taskDetails.completedNum / taskDetails.storeQuantity) * 100"
+              :size="50"
+              color="rgba(66, 113, 255, 1)"
+              :clockwise="false"
+            >
+              <div class="flex flex-col items-center">
+                <div class="w-[29.20px] h-[18.39px] text-black text-sm font-medium font-['DIN']">
+                  {{ (taskDetails.completedNum / taskDetails.storeQuantity) * 100 }}%
+                </div>
+                <div
+                  class="w-[22.71px] h-[10.82px] text-black/60 text-[7px] font-normal font-['PingFang SC']"
+                >
+                  达成率
+                </div>
+              </div>
+            </wd-circle>
           </div>
           <div class="flex flex-row items-center justify-start ml-[37px]">
             <div class="text-black/60 text-sm font-normal font-['PingFang_SC']">奖励积分:</div>
-            <div class="text-[#ff2d2d] text-[22px] font-medium font-['DIN'] leading-normal">
-              {{ taskDetails?.pointsReward }}
-            </div>
-            <div class="text-[#ff2d2d] text-xs font-normal font-['PingFang_SC'] leading-normal">
-              积分
+            <div class="flex items-end gap-1">
+              <div class="text-[#ff2d2d] text-[22px] font-medium font-['DIN'] leading-22px">
+                {{ taskDetails?.pointsReward }}
+              </div>
+              <div class="text-[#ff2d2d] text-xs font-normal font-['PingFang_SC']">积分</div>
             </div>
           </div>
         </div>
@@ -190,7 +217,7 @@ onLoad(async (query: { taskId: string }) => {
           </div>
         </div>
         <div class="flex items-center justify-start gap-3">
-          <template v-for="item in taskDetails?.brokerList">
+          <template v-for="item in taskDetails?.brokerList" :key="item.id">
             <div class="my-[23px]">
               <img class="w-[46px] h-[46px] rounded-full" :src="item.headImgUrl" alt="" />
               <div
@@ -208,8 +235,8 @@ onLoad(async (query: { taskId: string }) => {
         <div class="mr-2.5 w-1 h-4 rotate-180 bg-[#2357e9] rounded-[20px]"></div>
         <SectionHeading title="数据明细" size="base"></SectionHeading>
       </div>
-      <div v-if="taskDetails?.finalType == 1" class="flex flex-col gap-4 mt-5">
-        <template v-for="item in taskDetails?.brokerList">
+      <div v-if="taskDetails?.finalType == '1'" class="flex flex-col gap-4 mt-5">
+        <template v-for="item in taskDetails?.brokerList" :key="item.id">
           <div class="flex gap-2.5 p-3.5 bg-[#f7fbff] items-center rounded-[10px]">
             <img class="w-11 h-11 rounded-full" :src="item.headImgUrl" />
             <div class="flex-1 flex flex-col gap-2">
@@ -229,13 +256,13 @@ onLoad(async (query: { taskId: string }) => {
         </template>
       </div>
       <div v-else class="bg-[#f7fbff] rounded-[10px] py-[22px] px-[16px] mt-[20px]">
-        <template v-for="item in taskDetails?.reportList">
+        <template v-for="(item, i) in taskDetails?.reportList" :key="i">
           <div class="flex items-center justify-between">
             <div class="text-black/40 text-xs font-normal font-['PingFang SC'] leading-normal">
               {{ dayjs(item.createTime).format('YYYY/MM/DD HH:mm') }}
             </div>
             <div class="text-[#2357e9] text-xs font-normal font-['PingFang SC'] leading-normal">
-              {{ item.status == 0 ? '审核通过' : item.status == 1 ? '审核中' : '驳回' }}
+              {{ item.status == '0' ? '审核通过' : item.status == '1' ? '审核中' : '驳回' }}
             </div>
           </div>
           <div class="mt-[7px]">
@@ -284,7 +311,7 @@ onLoad(async (query: { taskId: string }) => {
         </div>
       </template>
       <template v-else>
-        <template v-if="!taskDetails?.receive && taskDetails?.finalType == 2">
+        <template v-if="!taskDetails?.receive && taskDetails?.finalType == '2'">
           <div class="px-5 py-3 bg-[#2357e9] rounded-md justify-center items-center gap-1">
             <div
               @click="publishState = true"
@@ -299,7 +326,12 @@ onLoad(async (query: { taskId: string }) => {
 
     <wd-action-sheet v-model="publishState" title="" @close="publishState = false">
       <view class="flex flex-col p-4 mt-4">
-        <data-form :schema="customerSchema" v-model="formData"></data-form>
+        <data-form
+          ref="dataFormRef"
+          :schema="customerSchema"
+          :rules="customerRules"
+          v-model="formData"
+        ></data-form>
         <div><wd-button block :round="false" @click="submitTask">提交</wd-button></div>
       </view>
     </wd-action-sheet>

+ 0 - 122
packages/merchant/src/pages/mine/components/tasks-card.vue

@@ -1,122 +0,0 @@
-<script setup lang="ts">
-import { Task } from '../../../core/models/moment'
-import { taskCenterBg } from '../../../core/libs/pngs'
-defineProps({
-  customClass: {
-    type: String,
-    default: '',
-  },
-  items: {
-    type: Array as PropType<Task[]>,
-    default: () => [],
-  },
-})
-const styles = ref({
-  header: {
-    width: '100%',
-    height: '60px',
-    background: `url(${taskCenterBg}) -9px -9px`,
-  },
-  content: {
-    background: `url(${taskCenterBg}) -9px -69px no-repeat`,
-  },
-  main: {
-    background: `url(${taskCenterBg}) -9px -9px`,
-  },
-})
-</script>
-<template>
-  <view class="relative w-full box-border">
-    <view class="relative h-full mx--2.5 mb--6 mt--1 box-border">
-      <wd-img :width="'100%'" :height="'100%'" :src="taskCenterBg" mode="widthFix"></wd-img>
-    </view>
-    <view class="absolute left-0 right-0 top-1 bottom-6 z-1 p-3.5">
-      <view class="w-full h-full flex flex-col justify-between">
-        <div class="text-start text-black text-lg font-normal font-['PingFang_SC'] leading-normal">
-          任务中心
-        </div>
-        <div class="flex-grow mt-4 overflow-auto">
-          <template v-for="({ taskKey, taskValue, status }, i) in items" :key="i">
-            <div class="flex items-center my-6">
-              <div class="text-black/90 text-sm font-normal font-['PingFang_SC'] leading-normal">
-                {{ taskKey }}
-              </div>
-              <div
-                class="ml-1 text-[#dc753a] text-xs font-normal font-['PingFang_SC'] leading-normal"
-              >
-                +{{ taskValue }}积分
-              </div>
-              <div class="flex-1"></div>
-              <wd-button type="info" plain size="small" :disabled="status">去完成</wd-button>
-            </div>
-          </template>
-        </div>
-      </view>
-    </view>
-  </view>
-  <!-- <div>
-    <div :style="styles.header">1</div>
-    <div :style="styles.content" class="p-3.5">
-      <template v-for="({ title, score }, i) in items" :key="i">
-        <div class="flex items-center my-6">
-          <div class="text-black/90 text-sm font-normal font-['PingFang_SC'] leading-normal">
-            {{ title }}
-          </div>
-          <div class="ml-1 text-[#dc753a] text-xs font-normal font-['PingFang_SC'] leading-normal">
-            +{{ score }}积分
-          </div>
-          <div class="flex-1"></div>
-          <wd-button type="info" plain size="small">去完成</wd-button>
-        </div>
-      </template>
-    </div>
-    <div></div>
-  </div> -->
-  <!-- <div class="card" :style="styles.main">
-    <div class="card-content">
-      <h2>任务中心</h2>
-      <ul>
-        <li>每日发布圈子获取积分</li>
-        <li>到店打卡获取积分</li>
-        <li>到店打卡获取积分</li>
-        <li>到店打卡获取积分</li>
-        <li>到店打卡获取积分</li>
-        <li>到店打卡获取积分</li>
-        <li>到店打卡获取积分</li>
-        <li>到店打卡获取积分</li>
-        <li>到店打卡获取积分</li>
-        <li>到店打卡获取积分</li>
-        <li>到店打卡获取积分</li>
-        <li>到店打卡获取积分</li>
-        <li>到店打卡获取积分</li>
-        <li>到店打卡获取积分</li>
-        <li>到店打卡获取积分</li>
-      </ul>
-    </div>
-  </div> -->
-</template>
-
-<style scoped>
-.card {
-  box-sizing: border-box;
-  display: flex;
-  flex-direction: column;
-  justify-content: flex-start;
-  width: 100%;
-  min-height: 200px;
-  padding: 20px;
-  /* background-image: url('your-image-path'); */
-  /* 背景图片路径 */
-  background-repeat: no-repeat;
-  background-position: top center; /* 背景图片头部固定 */
-  background-size: auto; /* 固定背景图片的宽度,不拉伸 */
-  border-radius: 10px;
-}
-
-.card-content {
-  flex-grow: 1;
-  padding: 20px;
-  background-color: rgba(255, 255, 255, 0.8); /* 半透明背景 */
-  border-radius: 10px;
-}
-</style>