snow пре 6 месеци
родитељ
комит
30896f9b90
40 измењених фајлова са 2511 додато и 251 уклоњено
  1. 9 0
      src/api/netOrderEnter/netOrderEntry/index.ts
  2. 10 0
      src/api/sellOut/project/index.ts
  3. 19 0
      src/api/sellOut/salesOrder/index.ts
  4. 10 6
      src/api/sellOut/zixunOrder/index.ts
  5. 5 2
      src/components/BasicForm/index.ts
  6. 2 1
      src/components/BasicForm/src/fields/addr-query.vue
  7. 3 2
      src/components/BasicForm/src/fields/category-flatten-query.vue
  8. 54 0
      src/components/BasicForm/src/fields/manager-query.vue
  9. 30 0
      src/components/BasicForm/src/fields/order-used-query.vue
  10. 11 34
      src/components/PageContainer/src/page-container.tsx
  11. 1 5
      src/components/PageContent/src/types.ts
  12. 4 0
      src/components/ReIcon/src/iconifyIconOffline.ts
  13. 3 1
      src/components/RemoteSelect/src/remote-select.tsx
  14. 28 2
      src/components/UploadFile/index.vue
  15. 8 0
      src/style/index.scss
  16. 339 0
      src/views/sellOut/project/components/makeProject.vue
  17. 3 1
      src/views/sellOut/project/detail.vue
  18. 25 4
      src/views/sellOut/salesOrder/components/AddressModal.vue
  19. 348 30
      src/views/sellOut/salesOrder/components/BasicForm.vue
  20. 4 1
      src/views/sellOut/salesOrder/components/ProductModal/config/content.config.ts
  21. 6 20
      src/views/sellOut/salesOrder/components/ProductModal/config/search.config.ts
  22. 37 7
      src/views/sellOut/salesOrder/components/ProductModal/index.vue
  23. 64 0
      src/views/sellOut/salesOrder/components/ProofModal/config/_options.ts
  24. 61 0
      src/views/sellOut/salesOrder/components/ProofModal/config/content.config.ts
  25. 27 0
      src/views/sellOut/salesOrder/components/ProofModal/config/search.config.ts
  26. 57 0
      src/views/sellOut/salesOrder/components/ProofModal/index.vue
  27. 59 0
      src/views/sellOut/salesOrder/components/ReturnModal.vue
  28. 0 0
      src/views/sellOut/salesOrder/components/importAddress.vue
  29. 60 0
      src/views/sellOut/salesOrder/components/upload-address/columns-config.ts
  30. 208 0
      src/views/sellOut/salesOrder/components/upload-address/index.vue
  31. 171 21
      src/views/sellOut/salesOrder/config/_options.ts
  32. 92 3
      src/views/sellOut/salesOrder/detail.vue
  33. 15 7
      src/views/sellOut/stockApply/detail.vue
  34. 6 1
      src/views/sellOut/stockApply/good-config/content.config.ts
  35. 7 8
      src/views/sellOut/stockApply/modal.vue
  36. 111 16
      src/views/sellOut/zixunOrder/components/activityProductModal.vue
  37. 488 66
      src/views/sellOut/zixunOrder/components/baseForm.vue
  38. 13 6
      src/views/sellOut/zixunOrder/config/_options.ts
  39. 93 4
      src/views/sellOut/zixunOrder/detail.vue
  40. 20 3
      src/views/sellOut/zixunOrder/index.vue

+ 9 - 0
src/api/netOrderEnter/netOrderEntry/index.ts

@@ -52,6 +52,15 @@ export const httpUsers= (data): any => {
   return http.request("post", `${yewuApi}userCompanyBasicList`, {  data: { ...data, noRela: true }});
 };
 
+
+export const httpManagerUsers= (data): any => {
+  return http.request("post", `${yewuApi}user_role_list`, {  data: { ...data, noRela: true }});
+};
+
+export const httpOrderUsed= (data): any => {
+  return http.request("post", `${yewuApi}uselist`, {  data: { ...data, noRela: true }});
+};
+
 export const httpUnits= (data): any => {
   return http.request("post", `${yewuApi}unitlist`, {  data: { ...data, noRela: true }});
 };

+ 10 - 0
src/api/sellOut/project/index.ts

@@ -16,6 +16,16 @@ export const httpCreate = (data: object): any => {
 export const httpDetail = (data: object): any => {
   return http.request("post", `${yewuApi}projectinfo`, { data });
 };
+export const httpFeedbackProducts = (data: object): any => {
+  return http.request("post", `${yewuApi}projectfeedlist`, { data });
+};
+export const httpMakePlan = (data: object): any => {
+  return http.request("post", `${yewuApi}plancreate`, { data });
+};
+export const httpPlans = (data: object): any => {
+  return http.request("post", `${yewuApi}projectplan`, { data });
+};
+
 export const httpRateEdit = (data: object): any => {
   return http.request("post", `${yewuApi}projectrate`, { data });
 };

+ 19 - 0
src/api/sellOut/salesOrder/index.ts

@@ -19,9 +19,28 @@ export const httpGoodupList = (data: object): any => {
 export const httpCreate = (data: object): any => {
   return http.request("post", `${yewuApi}projectcreate`, { data });
 };
+
 export const httpDetail = (data: object): any => {
   return http.request("post", `${yewuApi}saleinfo`, { data });
 };
+
+export const httpOnlineProduct  = (data: object): any => {
+  return http.request("post", `${yewuApi}goodinfo`, { data });
+};
+
+export const httpActivity = (data: object): any => {
+  return http.request("post", `${yewuApi}actquery`, { data });
+};
+
+
+export const httpMinimumPrice = (data: object): any => {
+  return http.request("post", `${yewuApi}salegetprice`, { data });
+};
+
+export const httpProofs = (data: object): any => {
+  return http.request("post", `${yewuApi}goodproof`, { data });
+};
+
 export const httpDelete = (data: object): any => {
   return http.request("post", `${yewuApi}branddel`, { data });
 };

+ 10 - 6
src/api/sellOut/zixunOrder/index.ts

@@ -7,20 +7,24 @@ const yewuApi = VITE_PROXY_USER_REAL + "/admin/";
 export const httpList = (data: object): any => {
   return http.request("post", `${yewuApi}consultlists`, { data });
 };
+
 export const httpCreate = (data: object): any => {
-  return http.request("post", `${yewuApi}projectcreate`, { data });
+  return http.request("post", `${yewuApi}consultcreate`, { data });
 };
+
 export const httpDetail = (data: object): any => {
-  return http.request("post", `${yewuApi}projectinfo`, { data });
+  return http.request("post", `${yewuApi}consultzxinfo`, { data });
+};
+
+export const httpStatus = (data: object): any => {
+  return http.request("post", `${yewuApi}consultbar`, { data });
 };
+
 export const httpDelete = (data: object): any => {
   return http.request("post", `${yewuApi}branddel`, { data });
 };
 
 export const httpUpdate = (data: object): any => {
-  return http.request("post", `${yewuApi}brandedit`, { data });
+  return http.request("post", `${yewuApi}zxedit`, { data });
 };
 
-export const httpStatus = (data: object): any => {
-  return http.request("post", `${yewuApi}brandstatus`, { data });
-};

+ 5 - 2
src/components/BasicForm/index.ts

@@ -10,7 +10,8 @@ import BrandQuey from "./src/fields/brand-query.vue"
 import AddrQuery from "./src/fields/addr-query.vue"
 import AccountQuery from "./src/fields/account-query.vue";
 import UnitQuery from "./src/fields/unit-query.vue";
-
+import ManagerQuery from "./src/fields/manager-query.vue";
+import OrderUsedQuery from "./src/fields/order-used-query.vue";  
 import BusinessTypeQuery from "./src/fields/business-type-query.vue";
 
 import CategoryFlattenQuery from "./src/fields/category-flatten-query.vue"
@@ -31,7 +32,9 @@ export {
   AccountQuery,
   AreaQuery,
   BusinessTypeQuery,
-  UnitQuery
+  UnitQuery,
+  ManagerQuery,
+  OrderUsedQuery
 };
 
 export {

+ 2 - 1
src/components/BasicForm/src/fields/addr-query.vue

@@ -19,7 +19,8 @@ onMounted(() => {
 });
 
 defineExpose({
-  initialData: data => (RemoteSelectRef.value as any).initalData(data)
+  initialData: data => (RemoteSelectRef.value as any).initalData(data),
+  getRecurrentValue:  data => (RemoteSelectRef.value as any).getRecurrentValue(data)
 });
 </script>
 

+ 3 - 2
src/components/BasicForm/src/fields/category-flatten-query.vue

@@ -22,7 +22,8 @@ onMounted(() => {
 });
 
 defineExpose({
-  initialData: data => (RemoteSelectRef.value as any).initalData(data)
+  initialData: data => (RemoteSelectRef.value as any).initalData(data),
+  getRecurrentValue: data => (RemoteSelectRef.value as any).getRecurrentValue(data)
 });
 </script>
 
@@ -35,7 +36,7 @@ defineExpose({
     :api="httpFlattenCatgorylist"
     request-prop="cat_name"
     response-label-prop="cat_name"
-    :response-val-prop="prop || 'id'"
+    :response-val-prop="prop || 'cat_id'"
     @item-change="val => emits('change', val)"
     @clear="emits('change')"
     :pro-data="() => ({ companyNo })"

+ 54 - 0
src/components/BasicForm/src/fields/manager-query.vue

@@ -0,0 +1,54 @@
+<script setup lang="ts">
+import { httpManagerUsers } from "/@/api/netOrderEnter/netOrderEntry";
+import { useUserInfo } from "/@/hooks/core/useUser";
+import { useVModel } from "@vueuse/core"
+import { ref } from "vue"
+
+const props = defineProps<{ companyNo: string, modelValue: string }>()
+const modelValue = useVModel(props, 'modelValue')
+
+const state = ref({ options: [], loading: false })
+
+const { userInfo } = useUserInfo()
+
+
+async function onSearch(nickname){
+  if(!nickname){ return }
+  state.value.loading = true
+  const { code, data } = await httpManagerUsers({ nickname, companyNo: props.companyNo, needRelation: true })
+  state.value.loading = false
+  if(code == 0){ state.value.options = (data?.list || []).map(item => ({  value: item.id, label: item.nickname, status: item.status })) }
+}
+
+async function handleSelection(){
+  const { nickname, id } = userInfo.value
+  await onSearch(nickname)
+  modelValue.value = id
+}
+
+</script>
+
+<template>
+  <div class="w-full flex justify-between">
+    <ElSelect 
+      remote  
+      filterable
+      class="flex-1"
+      reserve-keyword
+      v-model="modelValue"
+      :loading="state.loading"
+      :remote-method="onSearch"
+    >
+      <ElOption 
+        v-for="item in state.options"
+        :value="item.value"
+        :label="item.label"
+        :disabled="item.status != '1'"
+      />
+    </ElSelect>
+
+    <ElButton @click="handleSelection">
+      选择当前账号
+    </ElButton>
+  </div>
+</template>

+ 30 - 0
src/components/BasicForm/src/fields/order-used-query.vue

@@ -0,0 +1,30 @@
+<script setup lang="ts">
+import { ref } from "vue"
+import { useVModel } from "@vueuse/core"
+import { httpOrderUsed } from "/@/api/netOrderEnter/netOrderEntry";
+
+
+const props = defineProps<{ modelValue: string; }>()
+
+const options = ref([])
+const modelValue = useVModel(props, 'modelValue')
+
+
+async function requestOrderUsed(){
+  const { code, message, data } = await httpOrderUsed()
+  if(code == 0){ options.value = (data.list || []).map(item => ({  value: item.id, label: item.order_use, status: item.status })) }
+}
+
+requestOrderUsed()
+</script>
+
+<template>
+  <ElSelect v-model="modelValue" style="width: 100%">
+    <ElOption 
+      v-for="item in options" 
+      :value="item.value"
+      :label="item.label"
+      :disabled="item.status != '1'"
+    />
+  </ElSelect>
+</template>

+ 11 - 34
src/components/PageContainer/src/page-container.tsx

@@ -13,37 +13,16 @@ import {
 const PageContainer = defineComponent({
   name: "PageContainer",
   props: {
-    pageName: {
-      type: String,
-      default: ""
-    },
-    contentConfig: {
-      type: Object as PropType<ContentConfig>
-    },
-    searchConfig: {
-      type: Object as PropType<FormConfig>
-    },
-    modalConfig: {
-      type: Object as PropType<ModalConfig>
-    },
-    getContentRef: {
-      type: Function as PropType<(ref: PageContentInstance) => any>
-    },
-    getModalRef: {
-      type: Function as PropType<(ref: PageContentInstance) => any>
-    },
-    hooks: {
-      type: Object as PropType<Hooks>,
-      default: () => ({})
-    },
-    events: {
-      type: Object as PropType<Events>,
-      default: () => ({})
-    },
-    otherSearchParameter : {
-      type: Object as PropType<Events>,
-      default: () => ({})
-    }
+    pageName: { type: String, default: "" },
+    contentConfig: { type: Object as PropType<ContentConfig> },
+    searchConfig: { type: Object as PropType<FormConfig> },
+    modalConfig: { type: Object as PropType<ModalConfig> },
+    getContentRef: { type: Function as PropType<(ref: PageContentInstance) => any> },
+    getModalRef: { type: Function as PropType<(ref: PageContentInstance) => any> },
+    hooks: { type: Object as PropType<Hooks>, default: () => ({}) },
+    events: { type: Object as PropType<Events>, default: () => ({}) },
+    otherSearchParameter : { type: Object as PropType<Events>, default: () => ({}) },
+    isPageStart: { type: Boolean, default: false }
   },
   emits: ["content-select-change"],
   setup(props, { slots, emit, expose }) {
@@ -60,8 +39,6 @@ const PageContainer = defineComponent({
     function renderPageContent() {
       const { contentConfig, events, otherSearchParameter } = props;
       const { content: contentEvents = {} } = events as any;
-
-      console.log(otherSearchParameter,'🚀🚀')
       if (!contentConfig) return;
       return (
         <PageContent
@@ -142,7 +119,7 @@ const PageContainer = defineComponent({
 
     return () => {
       return (
-        <div class="w-full h-full">
+        <div class={`w-full h-full ${props.isPageStart ? 'page-container-start' : ''}`}>
           {renderPageSearch()}
           {renderPageContent()}
           {renderPageModal()}

+ 1 - 5
src/components/PageContent/src/types.ts

@@ -78,12 +78,8 @@ export interface ContentApis {
 }
 
 export type PageContentInstance = typeof PageContent & {
+  onBeforeAction: (type: ActionType, data?: any, responseCallback?: () => void) => void;
   getPageData: (params?: any) => void;
-  onBeforeAction: (
-    type: ActionType,
-    data?: any,
-    responseCallback?: () => void
-  ) => void;
   onSearch: () => void;
 };
 

+ 4 - 0
src/components/ReIcon/src/iconifyIconOffline.ts

@@ -48,6 +48,10 @@ import QuestionFilled from "@iconify-icons/ep/question-filled";
 import Sort from "@iconify-icons/ep/sort";
 import Management from "@iconify-icons/ep/management";
 
+import VideoPause from "@iconify-icons/ep/video-pause"
+
+
+addIcon('video-pause', VideoPause)
 addIcon("sort", Sort);
 addIcon("Management", Management);
 addIcon("download-line", downloadLine);

+ 3 - 1
src/components/RemoteSelect/src/remote-select.tsx

@@ -57,7 +57,9 @@ const RemoteSelect = defineComponent({
       if(code === 0){
         const _data = props.isRoot ? data : customProp ? data[customProp] : data.list;
         list.value = _data.map(item => ({ ...item, label: item[responseLabelProp], value: item[responseValProp], subLabel: item[subLabelProp] }));
-        return list.value[0][responseValProp]
+        const value =  list.value[0][responseValProp];
+        emit('itemChange', list.value.find(item => item[props.responseValProp] === value))
+        return value
       }else{
         return ''
       }

+ 28 - 2
src/components/UploadFile/index.vue

@@ -4,7 +4,7 @@ import { useVModel } from "@vueuse/core"
 import { ElMessage} from "element-plus"
 import { getToken } from "/@/utils/auth";
 import { useTask } from "/@/hooks/core"
-import { ref } from "vue"
+import { ref, watch } from "vue"
 
 const props = defineProps<{ modelValue: string }>()
 const modelValue = useVModel(props, 'modelValue')
@@ -28,6 +28,24 @@ function onDelete(){
   modelValue.value = ''
   file_name.value = ''
 }
+
+
+watch(() => modelValue.value, () => {
+  if(!modelValue.value) return;
+  const chunks = modelValue.value.split('/');
+  file_name.value = chunks[chunks.length -1];
+}, {
+  immediate: true
+})
+
+
+function onDownload(){
+  const link = document.createElement('a');
+  link.href = modelValue.value;
+  link.download = file_name.value;
+  link.click();
+
+}
 </script>
 
 
@@ -35,7 +53,15 @@ function onDelete(){
   <div class="flex">
     <div class="flex mr-[10px]">
       <p v-if="!modelValue">暂未上传文件</p>
-      <a v-else :src="modelValue" target="_blank" :underline="false">{{ file_name }}</a>
+
+      <a 
+        style="overflow:hidden;white-space:nowrap; text-overflow: ellipsis; color: #409eff" 
+        v-else 
+        :src="modelValue" 
+        target="_blank" 
+        :underline="false"
+        @click="onDownload"
+      >{{ file_name }}</a>
     </div>
     <ElUpload :show-file-list="false" :before-upload="() => true"  accept=".pdf"  :http-request="handleUpload">
       <template #trigger>

+ 8 - 0
src/style/index.scss

@@ -151,3 +151,11 @@ html {
 body {
   font-family: robotothin,-apple-system,Helvetica Neue,Helvetica,Arial,PingFang SC,Hiragino Sans GB,STHeiti,Microsoft YaHei,Microsoft JhengHei,SimSun,sans-serif !important;
 }
+
+
+
+.page-container-start{
+  .el-pagination {
+    justify-content: flex-start !important;
+  }
+}

+ 339 - 0
src/views/sellOut/project/components/makeProject.vue

@@ -0,0 +1,339 @@
+<script setup lang="ts">
+import { ref } from "vue"
+import { statusOptions } from "./../config/_options"
+import { httpFeedbackProducts, httpMakePlan, httpPlans } from "/@/api/sellOut/project";
+import { useRenderIcon } from "/@/components/ReIcon/src/hooks";
+
+import { addition, multiplication  } from "/@/utils/calc";
+import { ElMessage } from "element-plus"
+import { useTask } from "/@/hooks/core"
+
+
+const props = defineProps<{ detail: any }>()
+
+const state = ref({ 
+  type: 'preview',
+  selected: [],
+  plans: [],
+  data: [], 
+  total: 0,
+  index: 0
+})
+
+const productsTask = useTask({ success:(data) => {
+  state.value.data = data.list
+  requestPlans()
+}})
+
+const plansTask = useTask({ success: (data = []) => {
+  state.value.plans = data
+  state.value.index = 0
+  state.value.selected = (data[state.value.index] || {}).feedback || []
+  calcPlanTotalPrice()
+} })
+
+const requestPlans = () => plansTask.run(httpPlans({ projectNo: props.detail.projectNo }))
+const makePlanTask = useTask({ success: () => {
+  state.value.type = 'preview'
+  requestPlans()
+} })
+
+function calcPlanTotalPrice(){
+  state.value.total = state.value.selected.reduce((prev, { sale_price, num }) => {
+    const current = Number(multiplication(sale_price, num)).toFixed(2)
+    return Number(addition(prev, current)).toFixed(2) 
+  }, 0)
+}
+
+function handleMoveIndex(step: number){
+  state.value.index += step  
+  state.value.selected = (state.value.plans[state.value.index] || {}).feedback || []
+  calcPlanTotalPrice()
+}
+
+/* 选择意向商品 */ 
+function handleSelectionChange(list: any[]){
+  state.value.selected = []
+  for(const item of list){
+    const origin_sale_price = item.sale_price
+    const new_sale_price = item.sale_price
+    const origin_num = item.num
+    state.value.selected.push({ ...item, origin_sale_price, new_sale_price, origin_num })
+  }
+}
+/* 单机添加方案句柄 */
+function handleCreateProject(){
+  state.value.selected = []
+  const { low_rate } = props.detail
+  const rate = low_rate || '0'
+  if(rate * 100 === 0){
+    ElMessage.warning('暂无项目毛利率,不能制定方案!')
+    return
+  }
+  state.value.type = 'create'
+}
+/* 单击保存方案的句柄 */
+function onCrateProject(){
+  const { selected } = state.value
+  let isOnly = true
+  for(const source of selected){
+    const item = selected.find(target => target.pgNo === source.pgNo && target.id !== source.id)
+    if(item) isOnly = false
+  }
+  if(!isOnly){
+    ElMessage.warning('一种商品要求只能选择一种商品!')
+    return
+  }
+
+  let isAll = true
+  for(const source of props.detail.ladder){
+    let to = 0
+    selected.forEach(target => { if(source.pgNo === target.pgNo) to++ })
+    if(to !== 1) isAll = false
+  }
+  if(!isAll){
+    ElMessage.warning('方案必须覆盖全部商品要求!')
+    return
+  }
+
+  const parameter = { feedback: [],  projectNo: props.detail.projectNo }
+  selected.forEach(item => parameter.feedback.push({
+    feedid: item.id,
+    good_num: item.num,
+    sale_price: item.new_sale_price
+  }))
+
+  makePlanTask.run(httpMakePlan(parameter))
+}
+
+
+/* 单击返回列表的句柄 */
+function handleBacklist(){
+  state.value.type = 'preview'
+  requestPlans()
+}
+
+function initialData(){
+  const parameter = { 
+    page: 1, 
+    size: 1000, 
+    zxNo: '', 
+    infoNo: '', 
+    bidNo: '', 
+    status: '1', 
+    pgNo: '', 
+    projectNo: props.detail.projectNo 
+  }
+  productsTask.run(httpFeedbackProducts(parameter))
+}
+
+
+initialData()
+</script>
+
+<template>
+  <div v-if="detail" v-loading="makePlanTask.loading || plansTask.loading">
+    <ElCard>
+      <div class="flex flex-col">
+        <!-- 商品要求-start -->
+        <h1 class="mb-[10px]">商品要求</h1>
+        <template v-if="detail.ladder">
+          <ElTable :data="detail.ladder" :size="'small'" border stripe style="width: 100%">
+            <ElTableColumn label="要求编码" prop="pgNo" width="155px" show-overflow-tooltip />
+            <ElTableColumn prop="good_type" label="商品类型" width="80px" show-overflow-tooltip>
+              <template #="{ row }">
+                <ElTag :size="'mini'">
+                  {{ (statusOptions.find((item) => item.value == row.good_type) || {}).label || '--' }}
+                </ElTag>
+              </template>
+            </ElTableColumn>
+            <ElTableColumn prop="budget_price" label="预算单价" width="110" show-overflow-tooltip />
+            <ElTableColumn prop="num" label="购买数量" width="110" show-overflow-tooltip />
+            <ElTableColumn prop="cat_name" label="商品分类" show-overflow-tooltip />
+            <ElTableColumn prop="good_img" label="图片" width="50" show-overflow-tooltip>
+              <template #="{ row }">
+                <ElImage 
+                  v-if="row.good_img"
+                  style="height:20px;width:20px"
+                  :teleported="true"
+                  :url-list="[row.good_img]"
+                  :src="row.good_img"
+                />
+              </template>
+            </ElTableColumn>
+            <ElTableColumn prop="good_name" label="商品名称" show-overflow-tooltip />
+          </ElTable>
+        </template>
+        <!-- 商品要求-end -->
+
+        <!-- 客户意向商品-start -->
+        <h1 class="my-[10px]">客户意向商品</h1>
+        <ElTable
+          border
+          stripe
+          :size="'small'"
+          ref="multipleTable"
+          style="width: 100%"
+          :data="state.data"
+          @selection-change="handleSelectionChange"
+          v-loading="productsTask.loading"
+        >
+          <ElTableColumn type="selection" width="40" v-if="state.type === 'create'"></ElTableColumn>
+          <ElTableColumn prop="pgNo" label="要求编码" width="155px" show-overflow-tooltip />
+          <ElTableColumn prop="sale_price" label="销售单价" width="110" show-overflow-tooltip />
+          <ElTableColumn prop="num" label="购买数量" width="110" show-overflow-tooltip />
+          <ElTableColumn prop="good_img" label="商品图片" min-width="168px" show-overflow-tooltip>
+            <template #="{ row }">
+                <ElImage 
+                  v-if="row.good_img"
+                  style="height:20px;width:20px"
+                  :teleported="true"
+                  :url-list="[row.good_img]"
+                  :src="row.good_img"
+                />
+              </template>
+          </ElTableColumn>
+          <ElTableColumn prop="good_name" label="商品名称" min-width="120" show-overflow-tooltip>
+            <template #="{ row }">
+              <span>{{ row.good_name }}</span>
+              <span
+                v-for="(si, sii) in row.specinfo"
+                :key="si.id + sii"
+              >{{ sii === 0 ? "_" : "-" }}{{ si.spec_name }}[{{ si.spec_value_name }}]</span>
+            </template>
+          </ElTableColumn>
+          <ElTableColumn prop="data_source" label="商品类型" width="85" show-overflow-tooltip>
+            <template #="{ row }">
+              <ElTag :size="'mini'" :type="row.data_source + '' === '1' ? 'success' : ''">
+                {{ row.data_source + '' === "1" ? "平台商品" : "采反商品" }}
+              </ElTag>
+            </template>
+          </ElTableColumn>
+          <ElTableColumn prop="class_cat" label="商品分类" show-overflow-tooltip />
+          <ElTableColumn prop="expire_day" label="信息有效期" width="85" show-overflow-tooltip />
+          <ElTableColumn prop="work_day" label="制作工期" width="70" show-overflow-tooltip />
+          <ElTableColumn prop="delivery_day" label="物流时间" width="70" show-overflow-tooltip />
+        </ElTable>
+        <!-- 客户意向商品-end -->
+
+        <!-- 项目方案-start -->
+        <div class="my-[10px] flex justify-between">
+          <h1 class="flex">
+            项目方案
+            <p v-if="state.type !== 'create' && state.plans.length !== 0" class="ml-[20px] color-red">
+              方案总金额: {{ state.total }}
+            </p>
+          </h1>
+
+          <ElButtonGroup  type="primary">
+            <ElButton v-if="state.type !== 'create'" :icon="useRenderIcon('add')" @click="handleCreateProject">
+              添加方案
+            </ElButton>
+
+            <template v-else>
+              <ElButton :icon="useRenderIcon('arrow-left-s-line')" @click="handleBacklist">
+               返回列表
+              </ElButton>
+              <ElButton @click="onCrateProject">
+                保存方案
+              </ElButton>
+            </template>
+
+            <template v-if="state.plans.length !== 0 && state.type !== 'create'">
+              <ElButton 
+                :icon="useRenderIcon('arrow-left-s-line')"
+                :disabled="state.index === 0"
+                @click="handleMoveIndex(-1)"
+              />
+              <ElButton>方案共{{ state.index + 1 }}/{{ state.plans.length }}</ElButton>
+              <ElButton 
+                :icon="useRenderIcon('arrow-right-s-line')"
+                :disabled="state.index === state.plans.length - 1"
+                @click="handleMoveIndex(1)"
+              />
+            </template>
+          </ElButtonGroup>
+        </div>
+
+        <ElTable
+          :data="state.selected" 
+          style="width: 100%"
+          :size="'small'"
+          border
+          stripe
+        >
+          <ElTableColumn prop="pgNo" label="要求编码" width="155px" show-overflow-tooltip />
+          <ElTableColumn
+            prop="origin_sale_price"
+            label="系统售价单价"
+            width="110"
+            show-overflow-tooltip
+          />
+
+          <ElTableColumn prop="new_sale_price" label="当前销售单价" width="150" show-overflow-tooltip>
+            <template #="{ row, $index }">
+              <ElInputNumber 
+                v-if="state.type === 'create'"
+                v-model="state.selected[$index].new_sale_price"
+                :min="0"
+                :precision="2"
+                placeholder="售价"
+                :max="100000000000"
+                controls-position="right"
+                size="small"
+              />
+
+              <span v-else>{{ row.new_sale_price || row.sale_price }}元</span>
+            </template>
+          </ElTableColumn>
+          <ElTableColumn prop="origin_num" label="竞价数量" width="130" show-overflow-tooltip />
+          <ElTableColumn prop="num" label="购买数量" width="150" show-overflow-tooltip>
+            <template #="{ row, $index }">
+              <ElInputNumber 
+                v-if="state.type === 'create'"
+                 v-model="state.selected[$index].num"
+                controls-position="right"
+                placeholder="购买数量"
+                :max="100000000000"
+                :precision="0"
+                size="small"
+                :min="row.origin_num"
+              />
+              <span v-else>{{ row.num }}</span>
+            </template>
+          </ElTableColumn>
+          <ElTableColumn prop="data_source" label="商品类型" width="85">
+            <template #="{ row }">
+              <ElTag
+                :size="'mini'"
+                :type="row.data_source + '' === '1' ? 'success' : ''"
+              >{{ row.data_source + '' === "1" ? "平台商品" : "采反商品" }}
+            </ElTag>
+            </template>
+          </ElTableColumn>
+          <ElTableColumn prop="good_img" label="商品图片" min-width="242px" show-overflow-tooltip>
+            <template #="{ row }">
+              <ElImage 
+                v-if="row.good_img"
+                style="height:20px;width:20px"
+                :teleported="true"
+                :url-list="[row.good_img]"
+                :src="row.good_img"
+              />
+            </template>
+          </ElTableColumn>
+          <ElTableColumn prop="good_name" label="商品名称" min-width="120" show-overflow-tooltip>
+            <template #="{ row }">
+              <span>{{ row.good_name }}</span>
+              <span v-for="(si, sii) in row.specinfo"  :key="si.id + sii">
+                {{ sii === 0 ? "_" : "-" }}{{ si.spec_name }}[{{ si.spec_value_name }}]
+              </span>
+            </template>
+          </ElTableColumn>
+          <ElTableColumn prop="class_cat" label="商品分类" show-overflow-tooltip />
+        </ElTable>
+        <!-- 项目方案-end -->
+      </div>
+    </ElCard>
+  </div>
+</template>

+ 3 - 1
src/views/sellOut/project/detail.vue

@@ -6,6 +6,7 @@ import { httpDetail } from "/@/api/sellOut/project";
 import GrossForm from "./components/grossForm.vue"
 import BaseForm from "./components/baseForm.vue"
 import Feedback from "./components/feedback.vue"
+import MakeProject from "./components/makeProject.vue"
 
 const pageName = 'projectDetail'
 const { id, isDetail, title, collapses } = useDetail({
@@ -68,7 +69,8 @@ onMounted(async () => {
           <Feedback :detail="detail" />
         </ElTabPane>
         
-        <ElTabPane label="方案制作" v-if="['3','4','5','6'].includes(String(detail.status))">
+        <ElTabPane label="方案制作" v-if="['3','4','5','6'].includes(String(detail.status))" lazy>
+          <MakeProject :detail="detail" />
         </ElTabPane>
       </template>
     </ElTabs>

+ 25 - 4
src/views/sellOut/salesOrder/components/AddressModal.vue

@@ -3,18 +3,19 @@ import { computed, watch, ref } from "vue"
 import { rules } from "../config/_options"
 import { useVModel } from "@vueuse/core"
 import { ElForm } from "element-plus"
+import { AddrQuery } from "/@/components/BasicForm"
 
 const emit = defineEmits(['submit'])
 
 const props = defineProps<{ 
-  visible: boolean, 
+  visible: boolean,
   index: number, 
   data: any 
 }>()
 
 const visible = useVModel(props, 'visible')
-
 const formRef = ref<InstanceType<typeof ElForm> | null>(null)
+const addrQueryRef = ref<InstanceType<typeof AddrQuery> | null>(null)
 
 const formData = ref({
   receipt_quantity: '',
@@ -30,7 +31,7 @@ const title = computed(() => {
   return `${base}收货信息`
 })
 
-watch(() => props.visible, () => {
+watch(() => props.visible, async () => {
   if(!props.visible) { return }
 
   const { 
@@ -38,18 +39,32 @@ watch(() => props.visible, () => {
     contactor = '',
     addr_code = '',
     mobile = '',
+    addr_name = '',
     addr = ''
   } = props.data
 
   formData.value = {
     receipt_quantity,
+    addr_name,
     addr_code,
     contactor,
     mobile,
     addr
    }
+
+
+   if(addr_name){
+    const result = await addrQueryRef.value.getRecurrentValue({ name: addr_name })
+    formData.value = result
+  }
+
 })
 
+
+function handleAddrChange({ search_name = '' }){
+  formData.value.addr_name = search_name
+}
+
 async function onSubmit(){
   try{
     await formRef.value?.validate()
@@ -90,7 +105,13 @@ async function onSubmit(){
       </ElFormItem>
 
       <ElFormItem label="收货省市区" prop="addr_code">
-        <ElInput placeholder="收货省市区" v-model="formData.addr_code" />
+        <AddrQuery
+          ref="addrQueryRef"
+          v-model="formData.addr_code"
+          @change="handleAddrChange"
+          placeholder="收货省市区"  
+          :level="3"
+       />
       </ElFormItem>
 
       <ElFormItem label="详细地址" prop="addr">

+ 348 - 30
src/views/sellOut/salesOrder/components/BasicForm.vue

@@ -1,29 +1,63 @@
 <script setup lang="ts">
-import { ref, reactive } from "vue"
-import { BusinessQuery, CustomerQuery, PlatformQuery } from "/@/components/BasicForm"
+import { ref, nextTick } from "vue"
 import { useRenderIcon } from "/@/components/ReIcon/src/hooks";
-import { ElMessage, ElForm } from "element-plus"
+import { httpOnlineProduct, httpActivity, httpMinimumPrice } from "/@/api/sellOut/salesOrder";
+import UploadAddress from "./upload-address/index.vue"
+import ProductModal from "./ProductModal/index.vue"
 import { useCurrentCompany } from "/@/hooks/core"
-import { tryOnMounted } from "@vueuse/core"
+import { ElMessage, ElForm } from "element-plus"
 import AddressModal from "./AddressModal.vue"
-import ProductModal from "./ProductModal/index.vue"
-import { basicRules } from "./../config/_options"
+import { tryOnMounted } from "@vueuse/core"
+import BasicDescriptions from "/@/components/BasicDescriptions";
+import { basicRules, productDetailColumns, goodTypeOptions2 } from "./../config/_options"
+import ProofModal from "./ProofModal/index.vue"
+
+import { UnitInput, NumberInput } from "/@/components/Input"
+import { BusinessQuery, CustomerQuery, PlatformQuery, ManagerQuery, OrderUsedQuery } from "/@/components/BasicForm"
+
+import { cloneDeep } from "lodash-unified";
+
+
+
 
 const { current } = useCurrentCompany()
+const formRef = ref<InstanceType<typeof ElForm> | null>(null)
+const goodTypeOptionsRef = ref(cloneDeep(goodTypeOptions2))
+const sendTypeOptions = [{ value: '1', label: '有地址下单' }, { value: '2', label: '无地址下单' }]
 
 
 
 const state = ref({ 
+  batchModalVisible: false,  /** 批量上传收货地址模态框显示状态 */ 
+
   addressModalVisible: false, /** 收货地址模态框显示状态 */ 
   index: -1,  /** 收货地址当前编辑项的索引 -1为创建 */ 
   data: {}, /** 收货地址便编辑当前项的初始值 */
 
-
   productModalVisible: false, /** 选择商品模态框显示状态 */
-  platformType: '' /** 当前平台的对接类型  1为有赞 2为BBC */
+  productDetail: {},
+
+
+  platformType: '', /** 当前平台的对接类型  1为有赞 2 为BBC */
+
+
+  proofModalVisible: false /** 凭证模态框显示状态 */
 })
 
 const formData = ref({
+  useid: '',
+  activity_stock: '0',
+  good_price: '0',
+  good_num: '0',
+  new_good_price: '0',
+
+  managerid: '',
+  proof_id: '',
+  proof_url: '',
+  proof_type: '',
+  spuCode: '',
+  goodtype: '',
+  good_code: '',
   platform_order: '',
   customer_code: '',
   platform_id: '',
@@ -33,23 +67,32 @@ const formData = ref({
   addr: []
 })
 
+const orderGoodNum = ref({
+  ogood_type: "",
+  ogood_num: '0'
+})
+
 function handleGoodChange(){
   if(!formData.value.platform_id){
     ElMessage.warning('请先选择平台')
     return
   }
+  
   if(!formData.value.sell_code){
     ElMessage.warning('请选择销售方公司')
     return
   }
+  
   if(state.value.platformType === '1'){
     ElMessage.warning('当前业务不支持对接有赞平台的商品')
     return
   }
+  
   if(state.value.platformType === '2'){
     ElMessage.warning('当前业务不支持对接BBC平台的商品')
     return
   }
+
   state.value.productModalVisible = true
 }
 
@@ -82,6 +125,152 @@ function handleCreateAddress(){
   state.value.addressModalVisible = true
 }
 
+async function requestProductsActivity(){
+  const { good_code, sell_code, platform_id } = formData.value
+
+  const parameter = {
+    supplierNo: sell_code,
+    skuCode: good_code,
+    platform_id
+  }
+
+  const { code, data, message } = await httpActivity(parameter)
+
+  if(code == 0){
+    const { act, good } = data
+    const actlist = act.length > 0 ? JSON.parse(JSON.stringify(act)) : []
+
+    actlist.forEach(item => {
+      item.value = item.activity_code;
+      item.label = item.activity_name;
+    })
+
+    goodTypeOptionsRef.value[1].children = []
+    goodTypeOptionsRef.value[1].children.push(...actlist)
+    const goodItem = good.length > 0 ? cloneDeep(good[0]) : {}
+
+    if(goodItem.good_name){
+      goodTypeOptionsRef.value[0].children.map(item => {
+        item.activity_stock = goodItem ? String(goodItem.stock_num) : "0"
+        return item
+      })
+    }
+  }else{
+    ElMessage.warning(message)
+  }
+
+}
+
+async function handleProductSelection(product){
+  formData.value = {
+    ...formData.value,
+    proof_id: '',
+    proof_url: '',
+    proof_type: '',
+    spuCode: product.spuCode,
+    good_code: product.skuCode
+  }
+  
+  nextTick(() => formRef.value.validateField('good_code'))
+  
+  const result = await httpOnlineProduct({ skuCode: formData.value.good_code })
+  state.value.productDetail = result.data
+  requestProductsActivity()
+}
+
+async function requestMinimumPrice(){
+  const result = { ok: true, price: '0' }
+  const {  good_num, good_code, goodtype } = formData.value
+
+  const key_1 = goodtype[1]
+  const key_0 = goodtype[0]
+
+
+  const parameter = { 
+    skuCode: good_code,
+    sale_num: good_num,
+    is_activity: key_0 === "is_activity_0" ? "0" : "1",
+    act_code: key_0 === "is_activity_1" ? key_1 : ""
+  }
+
+
+  const { code, message, data } = await httpMinimumPrice(parameter)
+
+
+  if(code === 0){ 
+    const { sale_price } = data
+    result.price = sale_price + ""
+    const oldGoodType = goodtype.length === 2 ? goodtype[1] : ""
+
+
+    if(key_0 === 'is_activity_0' && (oldGoodType == 2 || oldGoodType == 3)){
+      result.price = '0'
+    }
+
+  } else {
+    result.ok = false
+    ElMessage.warning(message)
+  }
+
+  return result
+}
+
+async function requestProductNewPrice(){
+  const { goodtype, good_num } = formData.value
+  const oldGoodType = goodtype.length === 2 ? goodtype[1] : ''
+  
+  if(oldGoodType == 2 || oldGoodType == 3){
+    formData.good_price = 0
+  }
+
+  const { ogood_type, ogood_num } = orderGoodNum.value
+  if(oldGoodType === ogood_type && good_num === ogood_num){
+    this.order_good_num = { ogood_type: goodtype.length === 2 ? goodtype[1] : '', ogood_num: formData.value.good_num };
+    return
+  }
+
+  if(goodtype.length === 2){
+    goodTypeOptionsRef.value.forEach(item => {
+      if(item.children && item.children.length){
+        item.children.forEach(child => {
+          if(child.value === goodtype[1]){
+            formData.value.activity_stock = child.activity_stock
+          }
+        })
+      }
+    })
+  } else {
+    formData.value.activity_stock = "0"
+  } 
+
+
+  // 请求系统最低售价
+  const result = await requestMinimumPrice()
+  formData.value.good_price = result.ok ? result.price : 0
+}
+
+async function handleProductNumChange(num){
+  if(Number(num || '0') !== 0){
+    await requestProductNewPrice()
+    await requestProductsActivity()
+  }
+
+}
+
+function handleProductTypeChange(){
+  
+}
+
+
+async function submitForm(){
+ try {
+  await formRef.value?.validate()
+  console.log(formData.value)
+ } catch(err) {
+  console.log(err)
+ }
+}
+
 
 tryOnMounted(() => {
   formData.value.sell_code = current.value.companyNo
@@ -89,7 +278,7 @@ tryOnMounted(() => {
 </script>
 
 <template>
-  <ElForm :rules="basicRules">
+  <ElForm :rules="basicRules" ref="formRef" :model="formData">
     <ElRow :gutter="10">
       <ElCol :span="12">
         <ElFormItem label="销售方" prop="sell_code">
@@ -103,10 +292,14 @@ tryOnMounted(() => {
 
       <ElCol :span="12">
         <ElFormItem label="企业客户" prop="customer_code">
-          <CustomerQuery v-model="formData.customer_code" placeholder="企业客户" />
+          <CustomerQuery 
+            v-model="formData.customer_code" 
+            placeholder="企业客户" 
+          />
         </ElFormItem>
       </ElCol>
     </ElRow>
+
     <ElRow :gutter="10">
       <ElCol :span="6">
         <ElFormItem label="所属平台" prop="platform_id">
@@ -117,7 +310,7 @@ tryOnMounted(() => {
           />
         </ElFormItem>
       </ElCol>
-
+  
       <ElCol :span="6">
         <ElFormItem label="平台订单号" prop="platform_order">
           <ElInput v-model="formData.platform_order" placeholder="如: PO号" />
@@ -127,6 +320,7 @@ tryOnMounted(() => {
       <ElCol :span="6">
         <ElFormItem label="回款时间" prop="paytime">
           <ElDatePicker 
+            style="width: 100%"
             v-model="formData.paytime" 
             placeholder="回款时间"
             :disabledDate="time => time.getTime() < Date.now() - 100 * 60 * 60 * 24"
@@ -140,43 +334,144 @@ tryOnMounted(() => {
         </ElFormItem>
       </ElCol>
     </ElRow>
+
     <ElRow :gutter="10">
       <ElCol :span="12">
         <ElFormItem label="销售商品" prop="good_code">
-          <ElInput placeholder="销售商品" @focus="handleGoodChange" />
+          <ElInput
+            v-if="!formData.good_code"
+            placeholder="销售商品" 
+            @click="handleGoodChange"
+            v-model="formData.good_code"
+            readonly
+          />
+
+          <BasicDescriptions 
+            v-else
+            :data="state.productDetail"
+            :columns="productDetailColumns"
+          />
         </ElFormItem>
       </ElCol>
 
+      <ElCol :span="12"  v-if="formData.good_code">
+        <ElRow :gutter="10">
+          <ElCol :span="12">
+            <ElFormItem label="活动类型" prop="goodtype">
+              <ElCascader
+                v-model="formData.goodtype"
+                :options="goodTypeOptionsRef"
+                style="width: 100%"
+                :props="{ expandTrigger: 'hover' }"
+                @change="handleProductTypeChange"
+              />
+            </ElFormItem>
+          </ElCol>
+
+          <ElCol :span="12">
+            <ElFormItem label="比价凭证" prop="proof_id">
+              <div class="w-full flex justify-between">
+                <ul>
+                  暂无凭证
+                </ul>
+
+                <ElTooltip content="查看历史凭证">
+                  <ElButton 
+                    type="primary" 
+                    size="small" 
+                    link 
+                    :icon="useRenderIcon('search')"
+                    @click="state.proofModalVisible = true"
+                  />
+                </ElTooltip>
+              </div>
+            </ElFormItem>
+          </ElCol>
+
+          <ElCol :span="12">
+            <ElFormItem label="剩余库存" prop="activity_stock">
+              <NumberInput v-model="formData.activity_stock" disabled />
+            </ElFormItem>
+          </ElCol>
+
+          <ElCol :span="12">
+            <ElFormItem label="系统最低售价" prop="good_price">
+              <UnitInput unit="元" v-model="formData.good_price" disabled />
+            </ElFormItem>
+          </ElCol>
+
+          <ElCol :span="12">
+            <ElFormItem label="销售数量" prop="good_num">
+              <NumberInput 
+                v-model="formData.good_num" 
+                @change="handleProductNumChange"
+              />
+            </ElFormItem>
+          </ElCol>
+
+          <ElCol :span="12">
+            <ElFormItem label="销售单价" prop="new_good_price">
+              <NumberInput 
+                v-model="formData.new_good_price" 
+                :disabled="formData.good_price == '0'"
+              />
+            </ElFormItem>
+          </ElCol>
+        </ElRow>
+
+      </ElCol>
+
       <ElCol :span="12">
-        <ElFormItem label="业务经理">
-          <ElInput placeholder="业务经理">
-            <template #append>
-              <ElButton type="primary">选择当前账号</ElButton>
-            </template>
-          </ElInput>
+        <ElFormItem label="业务经理" prop="managerid">
+          <ManagerQuery 
+            v-model="formData.managerid"
+            placeholder="业务经理" 
+            :companyNo="current.companyNo" 
+          />
         </ElFormItem>
       </ElCol>
     </ElRow>
     <ElRow :gutter="10">
-      <ElCol :span="12"></ElCol>
+      <ElCol :span="12">
+      </ElCol>
+      
+      
       <ElCol :span="12">
         <ElRow :gutter="10">
           <ElCol :span="12">
-            <ElFormItem label="到货时间">
-              <ElDatePicker placeholder="到货时间" />
+            <!-- 
+              arrtime: "",
+              sendtype: "",
+              -->
+            <ElFormItem label="到货时间" prop="arrtime">
+              <ElDatePicker 
+                v-model="formData.arrtime" 
+                placeholder="到货时间"
+                style="width: 100%"
+              />
             </ElFormItem>
           </ElCol>
 
           <ElCol :span="12">
-            <ElFormItem label="下单方式">
-              <ElSelect style="width: 100%" placeholder="下单方式">
+            <ElFormItem label="下单方式" prop="sendtype">
+              <ElSelect
+                v-model="formData.sendtype"
+                style="width: 100%" 
+                placeholder="下单方式"
+              >
+                <ElOption 
+                  v-for="opt in sendTypeOptions"
+                  :value="opt.value"
+                  :label="opt.label"
+                  :key="opt.value"
+                />
               </ElSelect>
             </ElFormItem>
           </ElCol>
 
           <ElCol :span="24">
-            <ElFormItem label="订单用途">
-              <ElInput placeholder="订单用途" />
+            <ElFormItem label="订单用途" prop="useid">
+              <OrderUsedQuery v-model="formData.useid" placeholder="订单用途" />
             </ElFormItem>
           </ElCol>
         </ElRow>
@@ -184,7 +479,7 @@ tryOnMounted(() => {
     </ElRow>
     <ElRow>
       <ElCol :span="24">
-        <ElFormItem label="订单备注">
+        <ElFormItem label="订单备注" prop="remark">
           <ElInput type="textarea" placeholder="订单备注" />
         </ElFormItem>
       </ElCol>
@@ -195,16 +490,22 @@ tryOnMounted(() => {
           <ElTableColumn label="收货总数" min-width="80px" prop="receipt_quantity" />
           <ElTableColumn label="收货联系人" min-width="180px" prop="contactor" />
           <ElTableColumn label="收货联系电话" width="180px" prop="mobile" />
-          <ElTableColumn label="收货省市区" min-width="180px" prop="addr_code" />
+          <ElTableColumn label="收货省市区" min-width="180px" prop="addr_name" />
           <ElTableColumn label="详细地址" min-width="180px" prop="addr" />
           <ElTableColumn width="100px">
             <template #header>
-              <ElTooltip content="下载收货地址模板" placement="top">
+              <!-- <ElTooltip content="下载收货地址模板" placement="top">
                 <ElButton size="small" link type="primary" :icon="useRenderIcon('arrow-down-line')" />
               </ElTooltip>
               <ElTooltip content="导入收货地址" placement="top">
-                <ElButton size="small" link type="primary" :icon="useRenderIcon('arrow-up-line')" />
-              </ElTooltip>
+                <ElButton
+                  link 
+                  size="small" 
+                  type="primary" 
+                  :icon="useRenderIcon('arrow-up-line')"
+                  @click="state.batchModalVisible = true"
+                />
+              </ElTooltip> -->
 
               <ElTooltip content="手动添加地址" placement="top">
                 <ElButton link size="small" type="primary" :icon="useRenderIcon('add')" @click="handleCreateAddress" />
@@ -225,6 +526,13 @@ tryOnMounted(() => {
       </ElCol>
     </ElRow>
 
+    <ElRow>
+      <div class="w-full flex justify-between mt-[10px]">
+        <p class="font-bold">订单总金额: 0</p>
+        <ElButton type="primary" @click="submitForm">保存</ElButton>
+      </div>
+    </ElRow>
+
     <AddressModal 
       v-model:visible="state.addressModalVisible"  
       :index="state.index" 
@@ -235,6 +543,16 @@ tryOnMounted(() => {
     <ProductModal 
       v-model:visible="state.productModalVisible"
       :platformCode="formData.platform_id"
+      @submit="handleProductSelection"
+    />
+
+    <ProofModal  
+      v-model:visible="state.proofModalVisible" 
+      :spuCode="formData.spuCode"
+    />
+    
+    <UploadAddress 
+      v-model:visible="state.batchModalVisible" 
     />
   </ElForm>
 </template>

+ 4 - 1
src/views/sellOut/salesOrder/components/ProductModal/config/content.config.ts

@@ -39,7 +39,10 @@ const columns = [
   {
     prop: 'cat_name',
     label: '商品分类',
-    width: '120'
+    width: '120',
+    cellRenderer({ row }){
+      return row.cat_info.map(({ name }) => name).join('_')
+    }
   },
 
   {

+ 6 - 20
src/views/sellOut/salesOrder/components/ProductModal/config/search.config.ts

@@ -4,22 +4,9 @@ import { statusOptions } from "./_options";
 const searchFormConfig: FormConfig = {
   formItems: [
     {
-      field: "create_timer",
-      type: "date_picker",
-      label: '创建时间',
-      otherOptions: {
-        type: "daterange",
-        startProp: "start",
-        endProp: "end",
-        startPlaceholder: "开始时间",
-        endPlaceholder: "结束时间"
-      }
-    },
-    {
-      type: "select",
-      field: "status",
-      placeholder:'状态',
-      options: statusOptions
+      type: 'brand-query',
+      field: 'brandid',
+      placeholder: '品牌'
     },
     {
       field: "group",
@@ -27,11 +14,10 @@ const searchFormConfig: FormConfig = {
       type: "input_group",
       otherOptions: {
         inputGroupOptions: [
-          { value: "bk_code", label: "备库申请编号" },
-          { value: "good_code", label: "商品成本编号" },
           { value: "good_name", label: "商品名称" },
-          { value: "apply_name", label: "申请人" },
-          { value: "company_name", label: "申请人部门" }
+          { value: "spucode", label: "成本商品编码" },
+          { value: "skucode", label: "上线商品编码" },
+          { value: "plat_code", label: "平台商品编码" }
         ]
       }
     },

+ 37 - 7
src/views/sellOut/salesOrder/components/ProductModal/index.vue

@@ -1,30 +1,60 @@
 <script setup lang="ts">
 import { computed, ref  } from 'vue'
 import { useVModel } from "@vueuse/core"
-import contentConfig from "./config/content.config";
-
-import searchConfig from "./config/search.config";
-
+import searchConfig from "./config/search.config"
+import contentConfig from "./config/content.config"
 import { PageContent } from "/@/components/PageContent"
+import { ElMessage } from "element-plus"
+
+import { usePageSearch, type PageHooks, type PageEvents } from "/@/hooks/page";
 
+const emit = defineEmits(['submit', 'update:visible'])
 const props = defineProps<{ visible: boolean, platformCode: string }>()
-const pageContentRef = ref<InstanceType<typeof PageContent> | null>(null)
 
+
+const selected = ref([])
 const visible = useVModel(props, 'visible')
+const pageContentRef = ref<InstanceType<typeof PageContent> | null>(null)
 
 const parameter = computed(() => ({ 
   platform_code: props.platformCode, 
   exam_status: '3' 
 }))
+
+const hooks: PageHooks = {
+  pageSearchHook: () => usePageSearch(undefined, undefined, searchConfig)
+};
+
+function onSubmit(){
+  if(selected.value.length === 0){
+    ElMessage.warning('请选择至少一条数据')
+    return
+  }
+
+  if(selected.value.length > 1){
+    ElMessage.warning('只能选择一条数据')
+    return
+  }
+
+  emit('submit', selected.value[0])
+  visible.value = false
+}
 </script>
 
 <template>
-  <ElDialog v-model="visible" title="销售商品" top="10px" center>
+  <ElDialog class="relative" v-model="visible" title="选择已上线的商品" top="10px" center>
     <PageContainer
       v-if="visible"
+      :hooks="hooks"
       :searchConfig="searchConfig"
       :contentConfig="contentConfig"
       :otherSearchParameter="parameter"
+      @content-select-change="values => selected = values"
+      isPageStart
     />
-  </ElDialog>
+
+    <div class="absolute bottom-[40px] right-[35px]">
+      <ElButton type="primary" @click="onSubmit">保存</ElButton>
+    </div>
+ </ElDialog>
 </template>

+ 64 - 0
src/views/sellOut/salesOrder/components/ProofModal/config/_options.ts

@@ -0,0 +1,64 @@
+import { isChinese, isEmoticon, isMobile } from "/@/utils/validator";
+
+export const statusOptions = [
+  { value: "0", label: "待发货", type: 'warning' },
+  { value: "1", label: "待发货完成", type: 'warning' },
+  { value: "2", label: "发货已完成", type: 'success' },
+  { value: "3", label: "订单已取消", type: 'danger' }
+];
+export const platformTypeOptions = [
+  { value: '1', label: 'toB' },
+  { value: '2', label: 'toC' }
+];
+export const xsOrderSourceOptions = [
+  { value: "1", label: "直接下单" },
+  { value: "2", label: "竞价转单" },
+  { value: "3", label: "项目转单" },
+  { value: "4", label: "平台导入" },
+  { value: "5", label: "有赞平台" },
+  { value: "6", label: "售后补换货" },
+  { value: "7", label: "报备转单" },
+  { value: "9", label: "采销录单" },
+];
+export const xsOrderTypeOptions = [
+  { value: "1", label: "库存商品" },
+  { value: "2", label: "非库存商品" },
+  { value: "3", label: "反馈商品" },
+  { value: "4", label: "报备商品" },
+];
+function validatecontactor(_, value, callback){
+  if(value === '' || !value){
+    callback(new Error('联系人不能为空!'))
+  } else {
+    if(value.length < 2 || value.length > 10){
+      callback(new Error('联系人规则为2~10位汉字或字母!'))
+    } else {
+      const isOk = value.split('').every(s => isChinese(s) || /^[A-Za-z]+$/.test(s))
+      if(!isOk){
+        callback(new Error('联系人规则为2~10位汉字或字母!'))
+      } else if(isEmoticon(value)){
+        callback(new Error('联系人规则为2~10位汉字或字母!'))
+      } else {
+        callback()
+      }
+    }
+  }
+}
+function validatemobile(_, value, callback){
+  if(value === "" || !value) {
+    callback(new Error("手机号不能为空!"));
+  } else {
+    if(!isMobile(value)){
+      callback(new Error('手机号格式不正确!'))
+    } else {
+      callback()
+    }
+  }
+}
+export const rules = {
+  receipt_quantity: [{ required: true, trigger: 'blur', message: '收货数量不能为空' }],
+  contactor: [{ required: true, trigger: 'blur', validator: validatecontactor }],
+  mobile: [{ required: true, trigger: 'blur', validator: validatemobile }],
+  addr_code: [{ required: true,  message: '收货省市区不能为空', trigger: 'change'}],
+  addr: [{ required: true,  message: '收货省市区不能为空', trigger: 'blur' }]
+}

+ 61 - 0
src/views/sellOut/salesOrder/components/ProofModal/config/content.config.ts

@@ -0,0 +1,61 @@
+import { ContentConfig } from "/@/components/PageContent";
+import { httpProofs } from "/@/api/sellOut/salesOrder";
+import { ElImage, ElTag } from "element-plus";
+import { h } from "vue";
+
+const options = [
+  { value: '1', label: '视频' },
+  { value: '2', label: '图片' },
+  { value: '3', label: '其他' }
+]
+
+const columns = [
+  { type: 'selection', fixed: 'left', _noset_: true },
+  {
+    prop: 'spuCode',
+    label: '商品编码',
+    width: '160px'
+  },
+  {
+    label: '凭证类型',
+    width: '80px',
+    cellRenderer({ row }){
+      return h(ElTag, { size: 'small' }, {
+        default: () => options.find(item => item.value === row.proof_type)?.label || '--'
+      })
+    }
+  },
+  {
+    label: '比价凭证',
+    cellRenderer({ row }){
+      if(row.proof_type == '2'){
+        return h(ElImage, { 
+          style: { height: '25px', width: '25px'},
+          previewSrcList: [ row.proof_url],
+          previewTeleported: true,
+          src: row.proof_url
+        } , null)
+      }
+    }
+  },
+  {
+    prop: 'creater',
+    label: '创建人',
+    width: '120px'
+  },
+  {
+    prop: 'addtime',
+    label: '创建时间',
+    width: '120px'
+  }
+];
+
+const contentConfig: ContentConfig = {
+  title: "销售订单管理",
+  columns,
+  apis: {
+    httpList: httpProofs,
+  }
+};
+
+export default contentConfig;

+ 27 - 0
src/views/sellOut/salesOrder/components/ProofModal/config/search.config.ts

@@ -0,0 +1,27 @@
+import { FormConfig } from "/@/components/PageSearch";
+import { statusOptions } from "./_options";
+
+const searchFormConfig: FormConfig = {
+  formItems: [
+    // {
+    //   type: 'brand-query',
+    //   field: 'brandid',
+    //   placeholder: '品牌'
+    // },
+    // {
+    //   field: "group",
+    //   type_field: "group_type",
+    //   type: "input_group",
+    //   otherOptions: {
+    //     inputGroupOptions: [
+    //       { value: "good_name", label: "商品名称" },
+    //       { value: "spucode", label: "成本商品编码" },
+    //       { value: "skucode", label: "上线商品编码" },
+    //       { value: "plat_code", label: "平台商品编码" }
+    //     ]
+    //   }
+    // },
+  ]
+};
+
+export default searchFormConfig;

+ 57 - 0
src/views/sellOut/salesOrder/components/ProofModal/index.vue

@@ -0,0 +1,57 @@
+<script setup lang="ts">
+import { computed, ref  } from 'vue'
+import { useVModel } from "@vueuse/core"
+import searchConfig from "./config/search.config"
+import contentConfig from "./config/content.config"
+import { PageContent } from "/@/components/PageContent"
+import { ElMessage } from "element-plus"
+
+import { usePageSearch, type PageHooks, type PageEvents } from "/@/hooks/page";
+
+const emit = defineEmits(['submit', 'update:visible'])
+const props = defineProps<{ visible: boolean, spuCode: string }>()
+
+
+const selected = ref([])
+const visible = useVModel(props, 'visible')
+const pageContentRef = ref<InstanceType<typeof PageContent> | null>(null)
+
+const parameter = computed(() => ({ spuCode: props.spuCode }))
+
+const hooks: PageHooks = {
+  pageSearchHook: () => usePageSearch(undefined, undefined, searchConfig)
+};
+
+function onSubmit(){
+  if(selected.value.length === 0){
+    ElMessage.warning('请选择至少一条数据')
+    return
+  }
+
+  if(selected.value.length > 1){
+    ElMessage.warning('只能选择一条数据')
+    return
+  }
+
+  emit('submit', selected.value[0])
+  visible.value = false
+}
+</script>
+
+<template>
+  <ElDialog class="relative" v-model="visible" title="历史凭证记录" top="10px" center>
+    <PageContainer
+      v-if="visible"
+      :hooks="hooks"
+      :searchConfig="searchConfig"
+      :contentConfig="contentConfig"
+      :otherSearchParameter="parameter"
+      @content-select-change="values => selected = values"
+      isPageStart
+    />
+
+    <div class="absolute bottom-[40px] right-[35px]">
+      <ElButton type="primary" @click="onSubmit">保存</ElButton>
+    </div>
+ </ElDialog>
+</template>

+ 59 - 0
src/views/sellOut/salesOrder/components/ReturnModal.vue

@@ -0,0 +1,59 @@
+<script lang="ts" setup>
+import { useVModel } from "@vueuse/core"
+
+const props = defineProps<{ visible: boolean }>()
+const visible = useVModel(props, 'visible')
+</script>
+
+<template>
+  <ElDialog v-model="visible" title="新建退货单" center>
+      <ElForm>
+        <ElRow gutter="20">
+          <ElCol :span="12">
+            <ElFormItem label="退货类型">
+              <ElSelect style="width: 100%">
+
+              </ElSelect>
+            </ElFormItem>
+          </ElCol>
+          
+          <ElCol :span="12">
+            <ElFormItem label="退货原因">
+              <ElSelect style="width: 100%">
+
+              </ElSelect>
+            </ElFormItem>
+          </ElCol>
+
+          <ElCol :span="24">
+            <ElFormItem label="退货备注">
+              <ElInput 
+                style="width: 100%" 
+                type="textarea" 
+                placeholder="退货备注" 
+              />
+            </ElFormItem>
+          </ElCol>
+
+          <ElCol :span="8">
+            <ElFormItem label="可退数量">
+              <ElInput placeholder="可退数量" />
+            </ElFormItem>
+          </ElCol>
+
+          <ElCol :span="8">
+            <ElFormItem label="退货数量">
+              <ElInput placeholder="退货数量" />
+            </ElFormItem>
+          </ElCol>
+
+          <ElCol :span="8">
+            <div class="w-full flex justify-end">
+              <ElButton type="primary">保存</ElButton>
+              <ElButton>取消</ElButton>
+            </div>
+          </ElCol>
+        </ElRow>
+      </ElForm>
+  </ElDialog>
+</template>

+ 0 - 0
src/views/sellOut/salesOrder/components/importAddress.vue


+ 60 - 0
src/views/sellOut/salesOrder/components/upload-address/columns-config.ts

@@ -0,0 +1,60 @@
+const initheaders = [
+  "收款方公司编码",
+  "收款方公司名称",
+  "资金编码",
+  "订单编码",
+  "认领资金"
+];
+
+export const requireHeaders = [
+  "收款方公司编码",
+  "资金编码",
+  "订单编码",
+  "认领资金"
+];
+
+export const requsetHeaderIdxs = [
+  "companyNo",
+  "tradNo",
+  "orderCode",
+  "trad_fee"
+];
+
+const columns = () => {
+  const list: any[] = [
+    {
+      type: "index",
+      width: "50",
+      fixed: "left",
+      label: "序号"
+    },
+    {
+      prop: "companyNo",
+      label: "收货总数"
+    },
+    {
+      prop: "companyName",
+      label: "收货联系电话"
+    },
+    {
+      prop: "tradNo",
+      label: "收货省市区(文件导入)"
+    },
+    {
+      prop: "orderCode",
+      label: "收货省市区(系统解析)"
+    },
+    {
+      prop: "trad_fee",
+      label: "详细地址"
+    },
+    {
+      prop: "todo10",
+      label: '操作 ',
+      width: '80px'
+    }
+  ];
+  return list;
+};
+
+export { initheaders, columns };

+ 208 - 0
src/views/sellOut/salesOrder/components/upload-address/index.vue

@@ -0,0 +1,208 @@
+<script setup lang="ts">
+import { ref, unref } from "vue";
+import { httpCapitalUpload } from "/@/api/InvoiceSales/capitalClaim";
+import { ElMessage, ElNotification } from "element-plus";
+import { responseHandle } from "/@/utils/responseHandle";
+import { execlUpload } from "/@/components/execlUpload";
+import { useCompany } from "/@/hooks/core/useCompany";
+import { useNav } from "/@/layout/hooks/nav";
+
+
+import { useVModel } from "@vueuse/core"
+
+
+import {
+  columns,
+  initheaders,
+  requireHeaders,
+  requsetHeaderIdxs
+} from "./columns-config";
+
+
+const emit = defineEmits(["onSuccess"]);
+const props = defineProps<{ visible: boolean }>()
+
+const visible = useVModel(props, 'visible');
+
+const { currentCompany } = useCompany();
+const columnsConfig = columns();
+const { logout } = useNav();
+const loading = ref(false);
+const tableData = ref([]);
+
+
+const handleClose = () => {
+  // execlFile.value = null;
+  // uploadRef.value.clearFiles();
+};
+
+
+const Uploadsuccess = ({ results, header }) => {
+  loading.value = true;
+
+  if (results.length === 0) {
+    ElMessage.error("表格无有效数据!");
+    loading.value = false;
+    return;
+  }
+
+  let headok = true;
+  console.log(header);
+  console.log(initheaders);
+  if (header.length !== initheaders.length) {
+    headok = false;
+  } else {
+    initheaders.forEach((si, sii) => {
+      if (si !== header[sii]) {
+        headok = false;
+      }
+    });
+  }
+  if (!headok) {
+    ElMessage.error("表头与导入模板不匹配!");
+    loading.value = false;
+    return;
+  }
+  tableData.value = [];
+
+  try {
+    results.forEach(v1 => {
+      const b = Object.values(v1);
+      let model = {
+        companyNo: b[0],
+        companyName: b[1],
+        tradNo: b[2],
+        orderCode: b[3],
+        trad_fee: b[4]
+      };
+
+      tableData.value.push(model);
+    });
+
+     const companies = [...new Set(tableData.value.map(({companyNo}) => companyNo))]
+     if(companies.length !== 1){
+      ElMessage.warning('上传数据的收款方公司必须为同一家公司')
+      tableData.value = []
+      return
+     }
+
+     if(companies[0] !== currentCompany.value.companyNo){
+      ElMessage.warning('上传数据的收款方公司与当前选中的业务公司不一致')
+      tableData.value = []
+      return
+     }
+
+    loading.value = false;
+  } catch (e) {
+    ElMessage.error("导入数据拼接有误!");
+    loading.value = false;
+  }
+};
+//提交
+const handleSubmit = async () => {
+  if (loading.value) return;
+  loading.value = true;
+  let isok = true;
+  tableData.value.forEach(key => {
+    for (let i in key) {
+      if (requsetHeaderIdxs.includes(i) && !key[i]) {
+        isok = false;
+      }
+    }
+  });
+  if (!isok) {
+    ElNotification({ title: "必填字段缺失!", type: "error" });
+    loading.value = false;
+    return;
+  }
+
+  const { code, message } = await httpCapitalUpload({ list: unref(tableData) });
+
+  responseHandle({
+    code,
+    message,
+    logout,
+    handler: () => {
+      emit("onSuccess");
+      loading.value = false;
+      ElMessage.success("订单认款数据导入成功!");
+      visible.value = false;
+    },
+    error: () => {
+      loading.value = false;
+      ElNotification({ title: message, type: "error" });
+    }
+  });
+};
+
+const cancel = () => {
+  tableData.value = [];
+  // supplierNo,
+  // supplier_name,
+};
+</script>
+
+<template>
+  <el-dialog
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    v-model="visible"
+    title="导入订单认款数据"
+    width="1040px"
+    top="8vh"
+    center
+    @close="handleClose"
+  >
+    <execlUpload @on-success="Uploadsuccess" v-if="tableData.length === 0" />
+    <el-table
+      stripe
+      border
+      size="small"
+      :data="tableData"
+      max-height="500px"
+      style="width: 100%"
+    >
+      <el-table-column
+        v-for="(si, sii) in columnsConfig"
+        :minWidth="si.minWidth"
+        show-overflow-tooltip
+        :fixed="si.fixed"
+        :prop="si.prop"
+        :type="si.type"
+        :width="si.width"
+        :key="sii"
+      >
+        <template #header>
+          <span v-if="!requireHeaders.includes(si.label) || si.label === '序号'">
+            {{ si.label }}
+          </span>
+          <p v-else>
+            <span style="color: #f56c6c; font-size: 14px">* </span>
+            {{ si.label }}
+          </p>
+        </template>
+      </el-table-column>
+    </el-table>
+    <div
+      flex
+      justify-end
+      gap-2
+      v-if="tableData.length !== 0"
+      style="padding: 10px 0 0 0"
+    >
+      <el-button size="small" @click="cancel">取消</el-button>
+      <el-button
+        size="small"
+        type="primary"
+        :loading="loading"
+        @click="handleSubmit"
+      >保存</el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<style lang="scss" scoped>
+:deep(.el-upload-list__item) {
+  display: none !important;
+}
+</style>

+ 171 - 21
src/views/sellOut/salesOrder/config/_options.ts

@@ -1,11 +1,10 @@
-import { truncate } from "lodash";
-import { isChinese, isEmoticon, isMobile } from "/@/utils/validator";
-
-import { ElTag } from "element-plus"
-import { h } from "vue";
 import { xs_order_source_options, xs_order_type_options } from "/@/utils/status";
-import { createTooltip } from "/@/utils/tootip";
+import { isChinese, isEmoticon, isMobile } from "/@/utils/validator";
 import { goodTypeOptions } from "../../project/config/_options";
+import { createTooltip } from "/@/utils/tootip";
+import { ElTag } from "element-plus";
+import { truncate } from "lodash";
+import { h } from "vue";
 
 export const statusOptions = [
   { value: "0", label: "待发货", type: 'warning' },
@@ -13,6 +12,7 @@ export const statusOptions = [
   { value: "2", label: "发货已完成", type: 'success' },
   { value: "3", label: "订单已取消", type: 'danger' }
 ];
+
 export const platformTypeOptions = [
   { value: '1', label: 'toB' },
   { value: '2', label: 'toC' }
@@ -24,6 +24,7 @@ export const xsOrderTypeOptions = [
   { value: "3", label: "反馈商品" },
   { value: "4", label: "报备商品" },
 ];
+
 function validatecontactor(_, value, callback){
   if(value === '' || !value){
     callback(new Error('联系人不能为空!'))
@@ -32,7 +33,6 @@ function validatecontactor(_, value, callback){
       callback(new Error('联系人规则为2~10位汉字或字母!'))
     } else {
       const isOk = value.split('').every(s => isChinese(s) || /^[A-Za-z]+$/.test(s))
-
       if(!isOk){
         callback(new Error('联系人规则为2~10位汉字或字母!'))
       } else if(isEmoticon(value)){
@@ -71,19 +71,70 @@ export const basicRules = {
   good_code: [{ required: true, trigger: 'blur', message: '请选择销售商品' }],
   remark: [{ required: true, trigger: 'blur', message: '请输入备注' }],
   arrtime: [{ required: true, message: "要求到货时间", trigger: "change" }],
-  sendtype: [{ required: true, message: "请选择发货方式", trigger: "change" }]
-}
-
-
-// const statusOptions = [
-//   { value: "0", label: "待发货" },
-//   { value: "1", label: "待发货完成" },
-//   { value: "2", label: "发货已完成" },
-//   { value: "3", label: "订单已取消" }
-// ]
+  sendtype: [{ required: true, message: "请选择发货方式", trigger: "change" }],
+  platform_order:[
+    {
+      trigger: 'change',
+      validator:(rule, value, callback) => {
+        const len = value.length
+        if (value === '' || !value) {
+          callback()
+        } else if (len < 0 || len > 255) {
+          callback(new Error('仅支持大写字母数字组合,多个编号使用逗号分隔(0~255位)!'))
+        } else if (/^(?=.*[A-Z])(?=.*\d)[A-Z\d]+(?:,[A-Z\d]+)*$/.test(value) == false) {
+          callback(new Error('仅支持大写字母数字组合,多个编号使用逗号分隔(0~255位)!'))
+        } else {
+          callback()
+        }
+      }
+    }
+  ],
+  managerid: [
+    {
+      required: true,
+      message: "请选择业务经理",
+      trigger: "change"
+    }
+  ],
+  supplierNo: [
+    {
+      required: true,
+      message: "请选择销售方公司",
+      trigger: "change"
+    }
+  ],
+  new_good_price: [
+    {
+      required: true,
+      message: "销售单价不能为空!",
+      trigger: "blur"
+    }
+  ],
+  proof_id: [
+    {
+      required: false,
+      message: "请选择比价凭证",
+      trigger: "change"
+    }
+  ],
 
+  goodtype: [
+    {
+      type: "array",
+      required: true,
+      message: "请选择活动类型",
+      trigger: "change"
+    }
+  ],
+  good_num: [
+    {
+      required: true,
+      message: "请选择活动类型",
+      trigger: "change"
+    }
+  ]
+}
 
-// xs_order_source_options
 export const columns = [
   { label: '订单编号', field: 'orderCode', span: 6 },
   { 
@@ -123,9 +174,7 @@ export const columns = [
   { 
     label: '购买方公司', 
     span: 12,
-    render(_, { customer_code, customerName }){
-      return createTooltip(customerName, '购买方公司编码:' + customer_code, 300)
-    } 
+    render: (_, { customer_code, customerName }) => createTooltip(customerName, '购买方公司编码:' + customer_code, 300)
   },
   { label: '所属平台', span: 12, field: 'platform_name' },
   { label: '创建人', span: 6, field: 'apply_name'},
@@ -173,3 +222,104 @@ export const columns = [
   { field: 'platform_order', label: '平台订单号', span: 24 },
   { field: 'workNo', label: '其他单号', span: 24 }
 ]
+
+
+
+export const productDetailColumns = [
+  {
+    field: 'good_name',
+    label: '商品名称',
+    span: 24
+  },
+  {
+    field: 'cat',
+    label: '分类',
+    span: 24,
+    render(_,  row){
+
+      console.log(row)
+      return (row.cat_info || []).map(({ name }) => name).join('_')
+    }
+  },
+  {
+    field: 'is_stock',
+    label: '是否库存品',
+    span: 8,
+    render(_, row){
+      return row.is_stock === '1' ? '是' : '否'
+    }
+  },
+
+  {
+    field: 'unit',
+    label: '单位',
+    span: 8
+  },
+  {
+    field: 'tax',
+    label: '税率',
+    append: '%',
+    span: 8
+  },
+
+  {
+    field: 'exclusive',
+    label: '专属类型',
+    _slot_: 'exclusive',
+    span: 12
+  },
+  {
+    field: 'brand_name',
+    label: '品牌',
+    span: 12
+  },
+  {
+    field: 'weight',
+    label: '商品总克重',
+    _slot_: 'weight',
+    span: 24
+  },
+  {
+    field: 'lead_time',
+    label: '工期',
+    span: 8
+  },
+  {
+    field: 'delivery_day',
+    label: '物流',
+    span: 8
+  },
+  {
+    field: 'purchase',
+    label: '采购人员',
+    span: 8
+  },
+  {
+    field: 'delivery_place_cn',
+    label: '发货地',
+    span: 24
+  },
+  {
+    field: 'after_sales',
+    label: '售后说明',
+    span: 24
+  }
+]
+
+
+export const goodTypeOptions2 = [
+  {
+    value: 'is_activity_0',
+    label: '不参与活动',
+    children: [
+      { value: '1', label: '常规商品' },
+      { value: '2', label: '赠品' },
+      { value: '3', label: '样品' }
+    ]
+  },
+  {
+    value: "is_activity_1",
+    label: '参与活动',
+    children: []
+  }
+] as any

+ 92 - 3
src/views/sellOut/salesOrder/detail.vue

@@ -1,16 +1,27 @@
 <script setup lang="ts">
 import { ref } from "vue"
 import { useTask } from "/@/hooks/core"
-import { tryOnMounted } from "@vueuse/core"
 import {  columns } from "./config/_options"
 import BasicForm from "./components/BasicForm.vue"
 import { useDetail } from "/@/hooks/core/useDetail"
 import { httpDetail } from "/@/api/sellOut/salesOrder";
 import BasicDescriptions from "/@/components/BasicDescriptions";
+import ReturnModal from "./components/ReturnModal.vue"
+
+
+import UploadAddressModal from "./components/upload-address/index.vue"
+
+import { useRenderIcon } from "/@/components/ReIcon/src/hooks";
 
 const pageName = 'salesOrderDetail'
-const { title, collapses, isDetail, id } = useDetail({ baseName: "销售订单", collapseLen: 5 });
+
+const visible = ref(false)
+const addrVisible = ref(false)
+
 const orderDetailTask = useTask({ initialData: {} })
+const { title, collapses, isDetail, id } = useDetail({ baseName: "销售订单", collapseLen: 5 });
+
+
 if(isDetail.value){ orderDetailTask.run(httpDetail({ id: id.value })) }
 </script>
 
@@ -21,10 +32,88 @@ if(isDetail.value){ orderDetailTask.run(httpDetail({ id: id.value })) }
         <ElCollapse v-model="collapses">
           <ElCollapseItem :title="title" name="1">
             <BasicForm v-if="!isDetail" />
-            <BasicDescriptions v-else :columns="columns" :data="orderDetailTask.data" />
+
+            <BasicDescriptions 
+              v-else 
+              :columns="columns" 
+              :data="orderDetailTask.data" 
+            />
           </ElCollapseItem>
+
+          <template v-if="isDetail">
+            <ElCollapseItem title="发货单" name="2">
+              <ElTable border size="small">
+                <ElTableColumn type="selection" width="55px" />
+                <ElTableColumn label="发货单号" />
+                <ElTableColumn label="状态" />
+                <ElTableColumn label="发货数量" />
+                <ElTableColumn label="联系人" />
+                <ElTableColumn label="联系电话" />
+                <ElTableColumn label="收货地址" />
+                <ElTableColumn label="发货时间" />
+                <ElTableColumn>
+                <template #header>
+                  <div class="flex">
+                    <ElTooltip placement="top" content="有地址批量退货">
+                      <ElButton 
+                        type="primary"
+                        size="small"
+                        link
+                        :icon="useRenderIcon('ppt')">
+                      </ElButton>
+                    </ElTooltip>
+
+                    <ElTooltip placement="top" content="无地址退货">
+                      <ElButton 
+                        type="primary"
+                        size="small"
+                        link
+                        :icon="useRenderIcon('delete')"
+                        @click="visible = true"
+                        >
+                      </ElButton>
+                    </ElTooltip>
+
+                    <ElTooltip placement="top" content="下载收货地址模板">
+                      <ElButton 
+                        type="primary"
+                        size="small"
+                        link
+                        :icon="useRenderIcon('arrow-down-line')">
+                      </ElButton>
+                    </ElTooltip>
+
+                    <ElTooltip placement="top" content="导入收货地址">
+                      <ElButton 
+                        type="primary"
+                        size="small"
+                        link
+                        :icon="useRenderIcon('arrow-up-line')"
+                        @click="() =>addrVisible = true"
+                      />
+                    </ElTooltip>
+
+                    <ElTooltip placement="top" content="有地址批量退货">
+                      <ElButton 
+                        type="primary" 
+                        size="small" 
+                        link 
+                        :icon="useRenderIcon('add')" 
+                      />
+                    </ElTooltip>
+                  </div>
+                </template>
+                </ElTableColumn>
+              </ElTable>
+            </ElCollapseItem>
+          </template>
         </ElCollapse>
       </ElTabPane>
     </ElTabs>
+
+
+    <ReturnModal v-model:visible="visible" />
+
+    <UploadAddressModal v-model:visible="addrVisible" />
   </div>
 </template>

+ 15 - 7
src/views/sellOut/stockApply/detail.vue

@@ -37,15 +37,13 @@ requestBargainDetail()
 function handleProductConfirm(value){
   productDetail.value = value
   const { spuCode, good_name } = value
-  formData['value']['good_name'] = good_name
-  formData['value']['good_code'] = spuCode
+  formData.value = { ...formData.value, good_code: spuCode, good_name }
 }
 
-
 async function onSubmit(){
   try{
     await formRef.value.validate()
-    createTask['run'](httpCreate(formData['value']))
+    createTask.run(httpCreate(formData.value))
   } catch(err) {
     console.log(err)
   }
@@ -61,6 +59,13 @@ async function onSubmit(){
             <ElCollapseItem name="1" :title="title" v-loading="bkDetailTask.loading">
               <BasicDescriptions :columns="bargainColumns" :data="bkDetailTask.data" />
             </ElCollapseItem>
+
+            <ElCollapseItem name="2" title="采购反馈-设置入库的仓库" v-if="bkDetailTask.data.status == '1'">
+              <BasicDescriptions
+                :data="{}" 
+                :columns="[]" 
+              />
+            </ElCollapseItem>
           </template>
 
           <ElCollapseItem 
@@ -68,7 +73,12 @@ async function onSubmit(){
             name="3" 
             title="新建备库申请单"
           >
-            <ElForm label-width="100px" ref="formRef" :rules="rules" :model="formData">
+            <ElForm
+              ref="formRef"
+              :rules="rules"
+              :model="formData"
+              label-width="100px" 
+            >
               <ElRow :gutter="10">
                 <ElCol :span="12">
                   <ElFormItem label="商品名称" prop="good_name">
@@ -92,8 +102,6 @@ async function onSubmit(){
                   </ElFormItem>
                 </ElCol>
 
-              <!-- -->
-
                 <ElCol :span="7">
                   <ElFormItem label-width="110px" label="最晚入库时间" prop="lastime">
                     <ElDatePicker

+ 6 - 1
src/views/sellOut/stockApply/good-config/content.config.ts

@@ -97,7 +97,12 @@ const contentConfig: ContentConfig = {
   title: "备货申请管理",
   columns,
   apis: {
-    httpList: httpGoodupList,
+    httpList: (parameter = {}) => httpGoodupList({
+      ...parameter,
+      is_stock: '1',
+      is_combind: '0',
+      status: '1'
+    }),
     httpAdd: true
   }
 };

+ 7 - 8
src/views/sellOut/stockApply/modal.vue

@@ -30,18 +30,17 @@ function onConfirm(){
 </script>
 
 <template>
-  <ElDialog top="10px" v-model="visible" title="选择商品" center>
+  <ElDialog class="relative" top="10px" v-model="visible" title="选择商品" center>
     <PageContainer
       v-if="visible"
       :contentConfig="contentConfig"
       :search-config="searchConfig"
       @contentSelectChange="value => selected = value"
-    >
-      <template #search_button>
-        <ElButton type="primary" size="small" @click="onConfirm">
-            保存
-        </ElButton>
-      </template>
-    </PageContainer>
+      isPageStart
+    />
+
+    <ElButton class="absolute right-[36px] bottom-[46px]" type="primary" @click="onConfirm">
+      保存
+    </ElButton>
   </ElDialog>
 </template>

+ 111 - 16
src/views/sellOut/zixunOrder/components/activityProductModal.vue

@@ -1,6 +1,6 @@
 <script setup lang="ts">
-import { ref, computed } from "vue"
 import { useVModel } from "@vueuse/core"
+import { ref, computed, shallowRef } from "vue"
 import { isMultiplePlaceOptions, isCustomizedOptions, bidRules } from "./../config/_options"
 import { CategoryFlattenQuery, BrandQuey, UnitQuery } from "/@/components/BasicForm"
 import { ImageSingleUpload } from "/@/components/ImageUpload";
@@ -8,9 +8,19 @@ import { useCompany } from "/@/hooks/core/useCompany";
 import SpecForm from "/@/components/SpecForm"
 import { ElForm } from "element-plus"
 
+import { UnitInput } from "/@/components/Input";
+
 const props = defineProps<{ visible: boolean; data: any; isProject: boolean; index: number }>()
 const emit = defineEmits(['submit'])
 
+
+const configOptions = ['证书', '包装盒', '绒布袋', '标签', '其他']
+const disabledDate = (time: any) =>  time.getTime() <= Date.now();
+
+const isGoldPriceOptions = [{ value: '0', label: '不启用' }, { value: '1', label: '启用' }]
+
+const nobleOptions = [{ value: '1', label: '18K' }, { value: '2', label: '24K' }, { value: '3', label: '白银' }]
+
 const formRef = ref<InstanceType<typeof ElForm> | null>(null)
 const visible = useVModel(props, 'visible')
 const data = useVModel(props, 'data')
@@ -20,11 +30,17 @@ const total = computed(() => {
   return Number(budget_price * num).toFixed(2)
 })
 
+function handleChange(value){
+  const { cat_name, is_gold } = value || {}
+  data.value.cat_name = cat_name
+  data.value.isNoble = is_gold
+}
+
 const { currentCompany } = useCompany()
 async function onSubmit(){
   try{
     await formRef.value.validate()
-    emit('submit',{ newData:data.value, index: props.index })
+    emit('submit',{ newData: data.value, index: props.index })
     visible.value = false
   } catch (err){
     console.log(err)
@@ -39,12 +55,15 @@ async function onSubmit(){
         <ElCol :span="6">
           <ElFormItem label="到货日期" prop="arrival_time">
              <ElDatePicker 
-              v-model="data.arrival_time" 
-              :disabled="isProject"
-              placeholder="到货日期" 
+                v-model="data.arrival_time"
+                :disabledDate="disabledDate"
+                 value-format="YYYY-MM-DD HH:mm:ss"
+                :disabled="isProject"
+                placeholder="到货日期" 
              />
           </ElFormItem>
         </ElCol>
+
         <ElCol :span="6">
           <ElFormItem label="是否多地" prop="is_addrs">
              <ElSelect placeholder="是否多地" v-model="data.is_addrs">
@@ -61,11 +80,12 @@ async function onSubmit(){
           <ElFormItem label="预算单价" prop="budget_price">
             <div class="flex">
               <ElInputNumber 
-                style="width:100%" 
-                placeholder="预算单价"  
-                controls-position="right" 
                 v-model="data.budget_price"
+                controls-position="right"
+                placeholder="预算单价"  
                 :disabled="isProject"
+                style="width:100%" 
+                :precision="2"
               />
               <span class="ml-[5px]">元</span>
             </div>
@@ -82,9 +102,10 @@ async function onSubmit(){
             />
           </ElFormItem>
         </ElCol>
+
         <ElCol :span="6">
           <ElFormItem label="是否定制" prop="is_custom">
-            <ElSelect style="width: 100%" placeholder="是否订单" v-model="data.is_custom">
+            <ElSelect style="width: 100%" placeholder="是否定制" v-model="data.is_custom">
               <ElOption 
                 v-for="opt in isCustomizedOptions"
                 :label="opt.label"
@@ -98,7 +119,13 @@ async function onSubmit(){
         <ElCol :span="6">
           <ElFormItem label="重量" prop="total_weight">
             <div class="flex">
-              <ElInputNumber style="width:100%"  controls-position="right" v-model="data.total_weight" />
+              <ElInputNumber 
+                style="width:100%"  
+                controls-position="right" 
+                :precision="3" 
+                v-model="data.total_weight" 
+              />
+
               <span class="ml-[5px]">g</span>
             </div>
           </ElFormItem>
@@ -120,7 +147,11 @@ async function onSubmit(){
       <ElRow :gutter="10">
         <ElCol :span="12">
           <ElFormItem label="商品分类" prop="cat_id">
-            <CategoryFlattenQuery v-model="data.cat_id" :companyNo="currentCompany.companyNo" />
+            <CategoryFlattenQuery
+              v-model="data.cat_id" 
+              :companyNo="currentCompany.companyNo"
+              @change="handleChange"
+            />
           </ElFormItem>
           <ElFormItem label="工艺说明" prop="cost_desc">
             <ElInput v-model="data.cost_desc" placeholder="工艺说明" />
@@ -144,8 +175,68 @@ async function onSubmit(){
         </ElCol>
       </ElRow>
 
+      <ElRow v-if="data.isNoble">
+        <ElCol :span="12">
+          <ElFormItem label="配置要求" prop="config">
+            <ElSelect placeholder="配置要求" v-model="data.config" style="width: 100%">
+              <ElOption 
+                v-for="(item, index) in configOptions"
+                :key="item + index"
+                :label="item"
+                :value="item"
+              />
+            </ElSelect>
+          </ElFormItem>
+        </ElCol>
+
+        <ElCol :span="6">
+          <ElFormItem label="金属克重" prop="specs_weight">
+            <UnitInput unit="g" v-model="data.specs_weight" />
+          </ElFormItem>
+        </ElCol>
+
+        <ElCol :span="6">
+          <ElFormItem label="启用金价" prop="is_gold_price">
+            <ElSelect v-model="data.is_gold_price"  style="width: 100%">
+              <ElOption 
+                v-for="item in isGoldPriceOptions"
+                :value="item.value"
+                :label="item.label"
+              />
+            </ElSelect>
+          </ElFormItem>
+        </ElCol>
+      </ElRow>
+
+      <ElRow v-if="data.isNoble">
+        <ElCol :span="16">
+          <ElFormItem label="其他配置要求" prop="other_config" label-width="110px">
+            <ElInput
+              v-model="data.other_config"
+              type="textarea" 
+              placeholder="其他配置要求"
+              maxlength="250"
+              show-word-limit
+              :rows="2"
+            />
+          </ElFormItem>
+        </ElCol>
+
+        <ElCol :span="8">
+          <ElFormItem label="金属种类" prop="metal_id">
+            <ElSelect placeholder="贵金属种类" style="width: 100%" v-model="data.metal_id">
+              <ElOption 
+                v-for="opt in nobleOptions" 
+                :value="opt.value" 
+                :label="opt.label" 
+              />
+            </ElSelect>
+          </ElFormItem>
+        </ElCol>
+      </ElRow>
+
       <ElRow>
-        <ElCol :span="9">
+        <ElCol :span="8">
           <ElFormItem label="商品图片" prop="good_img">
             <div class="flex justify-start">
               <ImageSingleUpload v-model="data.good_img" />
@@ -153,10 +244,14 @@ async function onSubmit(){
             </div>
           </ElFormItem>
         </ElCol>
-        <!-- <ElCol :span="9">
-          <ElFormItem label="附件" prop="enclosure_file"></ElFormItem>
-        </ElCol> -->
-        <ElCol :span="15">
+        
+        <ElCol :span="8">
+          <ElFormItem label="附件" prop="enclosure_file">
+            <UploadFile v-model="data.file_url" />
+          </ElFormItem>
+        </ElCol>
+
+        <ElCol :span="8">
           <div class="w-full h-full flex flex-col items-end">
             <h3 class="text-right font-bold mb-[5px]">竞价单总额: {{ total }}</h3>
             <div class="w-full flex justify-end">

+ 488 - 66
src/views/sellOut/zixunOrder/components/baseForm.vue

@@ -1,42 +1,210 @@
 <script setup lang="ts">
-import { ref , watch, reactive, shallowReactive } from "vue"
+import { ref , watch, reactive, shallowReactive, nextTick, computed  } from "vue"
 import { useRenderIcon } from "/@/components/ReIcon/src/hooks";
+import { isMultiplePlaceOptions, isCustomizedOptions, bidRules } from "./../config/_options"
 import { BusinessQuery, PlatformQuery, CustomerQuery, AreaQuery } from "/@/components/BasicForm";
-import { rules, biddingTypes, initialData, initialBidGoodData } from "./../config/_options"
+import { rules, biddingTypes, createInitialData, initialBidGoodData } from "./../config/_options"
+import { CategoryFlattenQuery, BrandQuey, UnitQuery } from "/@/components/BasicForm"
+import { UnitInput } from "/@/components/Input";
 import { httpDetail as httpProjectDetail } from "/@/api/sellOut/project";
 import ActivityProductModal from "./activityProductModal.vue"
 import ProjectModal from "./projectModal/index.vue"
+import SpecForm from "/@/components/SpecForm"
 import { useCompany } from "/@/hooks/core/useCompany";
 import { ElForm, ElMessage } from "element-plus"
+import { httpCreate, httpDetail, httpUpdate } from "/@/api/sellOut/zixunOrder";
+import { ImageSingleUpload } from "/@/components/ImageUpload";
+import { useRouter } from "vue-router"
 import { useTask } from "/@/hooks/core"
 
 
+const props = defineProps<{ isDetail: boolean, isCopy: boolean, isUpdate: boolean ,id: string }>()
+const emit = defineEmits(['getDetail'])
+
+
+const router = useRouter()
 const { currentCompany } = useCompany()
 
-const visible = shallowReactive({ project: false, activity: false })
+
+const total = computed(() => {
+  const { budget_price, num } = ( data.value.ladder[0] || {})
+  return Number(budget_price * num).toFixed(2)
+})
 
 const areaInfo = shallowReactive({ freedom: '', name: '' })
-const formRef = ref<InstanceType<typeof ElForm> | null>(null) 
+  
+const formRef = ref<InstanceType<typeof ElForm> | null>(null)
+const visible = shallowReactive({ project: false, activity: false })
 const areaQueryRef = ref<InstanceType<typeof AreaQuery> | null>(null)
 const customerQueryRef = ref<InstanceType<typeof CustomerQuery> | null>(null)
-const platformQueryRef = ref<InstanceType<typeof PlatformQuery> | null>(null)
+const categoryFlattenQueryRef = ref<InstanceType<typeof CategoryFlattenQuery> | null>(null)
+
 
-const data = ref({ ...initialData })
 const selection = ref([])
+const data = ref({ ...createInitialData() })
+const ladderTemp = reactive({ detail: {}, index: -1 })
+
+const createTask = useTask({ success(){ router.push('/sellOut/zixunOrder') }})
+
+const updateTask = useTask({ success(){ router.push('/sellOut/zixunOrder') }})
+
+
+const detailTask = useTask({ success: async(_data) => {
+  const { 
+      endtime, 
+      is_project, 
+      platform_code, 
+      companyNo, 
+      khName, 
+      khNo, 
+      area, 
+      arrival_time, 
+      is_addrs, 
+      budget_price, 
+      num, 
+      is_custom,
+      total_weight,
+      unit,
+      brand_id,
+      cat_id,
+      good_name,
+      cost_desc,
+      use_desc,
+      remark,
+      specinfo: _specinfo,
+      good_img,
+      enclosure_file, 
+      metal_id,
+      other_config,
+      is_gold_price,
+      specs_weight,
+      config,
+      can = []
+  } = _data
+
+  const { name: cat_name } = (can[2] || {}) 
+
+  const specinfo = []
+
+  _specinfo.forEach(item => {  
+    specinfo.push({ ...item, spec_label: item.spec_name, spec_value_label: item.spec_value_name, spec_id: item.specid  }) 
+  })
+
+
+  data.value = { 
+    endtime, 
+    is_project, 
+    platform_code, 
+    companyNo,
+    khNo,
+    ladder: [
+      { 
+        budget_price,
+        arrival_time, 
+        is_addrs,
+        num,
+        is_custom,
+        total_weight,
+        unit,
+        brand_id,
+        cat_id,
+        good_name,
+        cost_desc,
+        use_desc,
+        remark,
+        specinfo,
+        good_img,
+        file_url: enclosure_file,
+        metal_id,
+        other_config,
+        is_gold_price,
+        specs_weight,
+        config 
+      }
+    ]
+  }
+
+  if(area === '无限制' || area === '不限区域'){
+    areaInfo.freedom = '0'
+    areaInfo.name = '0'
+    data.value.area = ' '
+  }else{
+    areaInfo.freedom = ''
+    areaInfo.name = area
+    data.value.area = await areaQueryRef.value?.getRecurrentValue({ name: areaInfo.name })
+  }
+
+
+  data.value.khNo = await customerQueryRef.value?.getRecurrentValue({ companyName: khName })
+  emit('getDetail', _data)
+
+  nextTick(async () => {
+    const result = await categoryFlattenQueryRef.value?.getRecurrentValue({ cat_name })
+    data.value.ladder[0].cat_id = result
+  })
+}})
+
 
-const projectDemandTask = useTask({ initialData: {},  success: async (response: any) => {
+const projectDemandTask = useTask({ 
+ initialData: {}, 
+ success: async (response: any) => {
   const { khName, platform_code } = response
   data.value.platform_code = platform_code
   data.value.khNo = await customerQueryRef.value?.getRecurrentValue({ companyName: khName })
 }})
 
-const ladderTemp = reactive({ detail: {}, index: -1 })
 
 async function submit(){
+  if(data.value.ladder.length === 0){
+    ElMessage.warning('至少提供一条商品要求!')
+    return
+  }
+
   try{
     formRef.value && formRef.value.validate()
-    const { ladder } = data.value
+    const {
+      khNo,
+      ladder,
+      endtime,
+      companyNo
+    } = data.value
+
+    if(new Date(endtime).getTime() <= new Date().getTime()){
+      ElMessage.warning('竞价截止时间应该大于当前时间')
+      return
+    }
+
+    const model: Record<string, any> = JSON.parse(JSON.stringify(data.value))
+    model.companyNo = currentCompany.value.companyNo
+    
+    if(areaInfo.freedom === '0'){
+      model.area = '不限区域'
+    } else{
+      model.area = areaInfo.name
+    }
 
+    model.ladder = []
+    ladder.forEach(item => {
+      const { 
+        specs,
+        specinfo: _specinfo,
+        file_url: enclosure_file,
+        isNoble: _1, 
+        cat_name: _2,
+        ...rest 
+      } = item;
+
+      const specinfo = [];
+      (specs || _specinfo).forEach(item => { specinfo.push({ specid: item.spec_id, spec_value_id: item.spec_value_id }) });
+      model.ladder.push({ ...rest, enclosure_file ,specinfo })
+    })
+
+    if(props.isUpdate){
+      const { ladder = [], ...rest } = model
+      updateTask.run(httpUpdate({ infoNo: props.id, ...ladder[0], ...rest }))
+    } else {
+      createTask.run(httpCreate(model))
+    }
   }catch(err){
     console.log(err)
   }
@@ -61,17 +229,21 @@ function handleAddActivityProduct(){
   ladderTemp.index = -1
 }
 
+function handleUpdateLadder(index: number){
+  ladderTemp.index = index
+  ladderTemp.detail = {... data.value.ladder[index]}
+  visible.activity = true
+}
 
 const disabledDate = (time: any) =>  time.getTime() < Date.now() - 60 * 60 * 24 * 1000;
 const requestProjectDemands = (projectNo: string) => projectDemandTask.run(httpProjectDetail({ projectNo }))
 
-
 /** 购买方改变时查找客户的收货地址并复制 */ 
 async function handleCustomerChange({ area } = ({} as any)){
   if(area === '无限制' || area === '不限区域'){
     areaInfo.freedom = '0'
     areaInfo.name = '0'
-    data.value.area = ''
+    data.value.area = ' '
   }else{
     areaInfo.freedom = ''
     areaInfo.name = area
@@ -89,10 +261,18 @@ function handleIsProjectChange(isProject: string){
 function handleAreaChange(row){
   areaInfo.freedom = ''
   areaInfo.name = row.search_name
+  data.value.area = row.search_name
 }
 
 function handleFreedomChange(){
-  areaInfo.name = ''
+  areaInfo.name = '不限区域'
+  data.value.area = '不限区域'
+}
+
+
+const isNoble = ref(false)
+function handleCategoryChange(row){
+  isNoble.value = !!row.is_gold
 }
 
 watch(() => data.value.is_project, () => {
@@ -110,24 +290,28 @@ watch(() => currentCompany, () => {
   }, 
   { immediate: true, deep: true }
 )
+
+if(props.id){ detailTask.run(httpDetail({ infoNo: props.id })) }
 </script>
 
 <template>
-  <div style="width:100%">
+  <div style="width:100%" v-loading="createTask.loading || detailTask.loading || updateTask.loading">
     <ElForm 
       label-width="100px" 
       :rules="rules" 
       ref="formRef"
       :model="data"
+      :disabled="isDetail"
     >
       <ElRow :gutter="10">
         <ElCol :span="6">
           <ElFormItem label="竞价类型" prop="is_project">
-            <ElSelect 
+            <ElSelect
               v-model="data.is_project" 
               placeholder="竞价类型" 
               @change="handleIsProjectChange"
-              style="width: 100%">
+              style="width: 100%"
+              :disabled="isCopy || isUpdate">
               <ElOption 
                 v-for="opt in biddingTypes" 
                 :value="opt.value" 
@@ -153,56 +337,56 @@ watch(() => currentCompany, () => {
 
         <ElCol :span="12">
           <ElFormItem label="所属平台" prop="platform_code">
-             <PlatformQuery 
+             <PlatformQuery
                 ref="platformQueryRef" 
                 v-model="data.platform_code" 
                 placeholder="所属平台" 
-                :disabled="data.is_project === '1'" 
+                :disabled="data.is_project === '1' || isUpdate" 
               />
           </ElFormItem>
         </ElCol>
+      </ElRow>
+
+      <ElRow>
         <ElCol :span="12">
           <ElFormItem label="销售方" prop="companyNo">
             <BusinessQuery v-model="data.companyNo" disabled />
           </ElFormItem>
         </ElCol>
-        
+
         <ElCol :span="12">
           <ElFormItem label="购买方" prop="khNo">
              <CustomerQuery 
                 ref="customerQueryRef"
                 v-model="data.khNo"  
-                placeholder="购买方" 
-                :disabled="data.is_project === '1'"
+                placeholder="购买方"
+                :disabled="data.is_project === '1' || isUpdate"
                 @change="handleCustomerChange"
              />
           </ElFormItem>
         </ElCol>
-
-        <ElCol :span="24">
-          <ElFormItem label="收货区域" prop="area">
-            <div class="flex w-full">
-              <ElRadio 
-                v-model="areaInfo.freedom" 
-                :disabled="!data.khNo || data.is_project === '1'" 
-                @change="handleFreedomChange"
-                label="0">
-              不限</ElRadio>
-
-
-              <AreaQuery 
-                ref="areaQueryRef"  
-                v-model="data.area" 
-                :disabled="!data.khNo || data.is_project === '1'" 
-                style="width: 100%" 
-                @change="handleAreaChange" 
-              />
-            </div>  
-          </ElFormItem>        
-        </ElCol>
+      </ElRow>
+        
+       <ElFormItem label="收货区域" prop="area">
+         <div class="flex w-full">
+           <ElRadio 
+             v-model="areaInfo.freedom" 
+             :disabled="!data.khNo || data.is_project === '1'" 
+             @change="handleFreedomChange"
+             label="0">
+           不限</ElRadio>
+
+           <AreaQuery 
+             ref="areaQueryRef"  
+             v-model="data.area" 
+             :disabled="!data.khNo || data.is_project === '1'" 
+             style="width: 100%" 
+             @change="handleAreaChange" 
+           />
+         </div>  
+       </ElFormItem>
 
         <template v-if="data.is_project === '1'">
-          <ElCol :span="24">
             <ElFormItem label="项目信息">
               <ElAlert
                 @click="() => visible.project = true"
@@ -211,12 +395,11 @@ watch(() => currentCompany, () => {
                 type="warning" 
                 show-icon 
                 center 
+                :closable="false"
               />
             </ElFormItem>
-          </ElCol>
             
-          <ElCol :span="24">
-              <ElFormItem label="项目商品要求" label-width="100px">
+            <ElFormItem label="项目商品要求" label-width="100px">
                 <ElTable 
                   :data="projectDemandTask.data.ladder || []" 
                   @selection-change="value => selection = value"
@@ -228,7 +411,7 @@ watch(() => currentCompany, () => {
                   <ElTableColumn label="商品阶梯" width="70px"  show-overflow-tooltip>
                     <template #="{ $index }">{{ $index }}</template>
                   </ElTableColumn>
-                  <ElTableColumn label="商品类型" width="70px"  show-overflow-tooltip>
+                  <ElTableColumn label="商品类型" width="79px"  show-overflow-tooltip>
                     <template #="{ row }">
                       <ElTag>{{ Number(row.good_type) === 1 ? '竞品' : Number(row.good_type) === 2  ? '竞聘' : '--'}}</ElTag>
                     </template>
@@ -247,19 +430,18 @@ watch(() => currentCompany, () => {
                   </ElTableColumn>
                   <ElTableColumn label="商品名称" prop="good_name" min-width="180px" show-overflow-tooltip />
                 </ElTable>
-              </ElFormItem> 
-            </ElCol>
+            </ElFormItem> 
         </template>
 
-        <ElCol :span="24">
-          <ElFormItem label="竞价商品要求" prop="ladder" label-width="100px">
+        <ElRow v-if="!isDetail && !isCopy && !isUpdate">
+          <ElFormItem label="竞价商品要求" prop="ladder" label-width="120px">
             <ElTable border size="small" :data="data.ladder">
               <ElTableColumn label="商品阶梯">
                 <template #="{ $index }">{{ $index + 1 }}</template>
               </ElTableColumn>
               <ElTableColumn label="预算单价" prop="budget_price" />
               <ElTableColumn label="购买数量" prop="num" />
-              <ElTableColumn label="商品分类" prop="cat_id_name" />
+              <ElTableColumn label="商品分类" prop="cat_name" min-width="300px" show-overflow-tooltip />
               <ElTableColumn label="图片">
                 <template #="{ row }">
                   <ElImage 
@@ -271,12 +453,6 @@ watch(() => currentCompany, () => {
                 </template>
               </ElTableColumn>
 
-              <ElTableColumn 
-                label="商品名称" 
-                prop="good_name" 
-                minWidth="170" 
-              />
-
               <ElTableColumn
                 prop="good_name"
                 label="商品名称"
@@ -290,10 +466,10 @@ watch(() => currentCompany, () => {
               width="170"
             >
               <template #="{ row }">
-                <span v-for="(si, sii) in row.specs" :key="si.spec_name + sii">
+                <span v-for="(si, sii) in row.specs" :key="si.spec_label + sii">
                   <span v-if="sii !== 0">-</span>
-                    {{ si.spec_name }}:[{{ si.spec_value_name}}]
-                  </span >
+                  {{ si.spec_label }}:[{{ si.spec_value_label}}]
+                </span>
               </template>
             </ElTableColumn>
 
@@ -333,18 +509,264 @@ watch(() => currentCompany, () => {
                       />
                     </ElTooltip>
                   </div>
-                </template>                
+                </template>
+
+
+                <template #="{ $index }">
+                  <ElTooltip content="编辑" placement="top">
+                    <ElButton
+                      @click="handleUpdateLadder($index)"
+                      :icon="useRenderIcon('edits')" 
+                      type="primary" 
+                      link 
+                    />
+                  </ElTooltip>
+
+                  <ElTooltip content="删除" placement="top">
+                    <ElButton
+                      @click="data.ladder.splice($index, 1)"
+                      :icon="useRenderIcon('delete')" 
+                      type="danger" 
+                      link 
+                    />
+                  </ElTooltip>
+                </template>
               </ElTableColumn>
             </ElTable>
           </ElFormItem>
-        </ElCol>
+        </ElRow>
+
+        <template v-else>
+          <ElRow v-if="data.ladder.length > 0">
+            <ElCol :span="6">
+              <ElFormItem label="到货日期">
+                <ElDatePicker 
+                  style="width:100%"
+                  v-model="data.ladder[0].arrival_time"
+                  :disabledDate="disabledDate"
+                  value-format="YYYY-MM-DD HH:mm:ss"
+                  placeholder="到货日期" 
+                />
+              </ElFormItem>
+            </ElCol>
+
+            <ElCol :span="6">
+              <ElFormItem label="是否多地" prop="is_addrs">
+                 <ElSelect style="width: 100%" placeholder="是否多地" v-model="data.ladder[0].is_addrs">
+                    <ElOption 
+                      v-for="opt in isMultiplePlaceOptions" 
+                      :label="opt.label"
+                      :value="opt.value"
+                      :key="opt.value"
+                    />
+                 </ElSelect>
+              </ElFormItem>
+            </ElCol>
 
-        <ElCol :span="24">
+            <ElCol :span="6">
+              <ElFormItem label="预算单价" prop="budget_price">
+                <div class="flex">
+                  <ElInputNumber 
+                    v-model="data.ladder[0].budget_price"
+                    controls-position="right"
+                    placeholder="预算单价"  
+                    style="width:100%" 
+                    :precision="2"
+                  />
+                  <span class="ml-[5px]">元</span>
+                </div>
+              </ElFormItem>
+            </ElCol>
+
+            <ElCol :span="6">
+              <ElFormItem label="数量" prop="num">
+                <ElInputNumber
+                 v-model="data.ladder[0].num"
+                  style="width: 100%" 
+                  controls-position="right" 
+                  placeholder="数量"
+                />
+              </ElFormItem>
+            </ElCol>
+
+            <ElCol :span="6">
+              <ElFormItem label="是否定制" prop="is_custom">
+                <ElSelect style="width: 100%" placeholder="是否定制" v-model="data.ladder[0].is_custom">
+                  <ElOption 
+                    v-for="opt in isCustomizedOptions"
+                    :label="opt.label"
+                    :value="opt.value"
+                    :key="opt.value"
+                  />
+                </ElSelect>
+              </ElFormItem>
+            </ElCol>
+
+            <ElCol :span="6">
+              <ElFormItem label="重量" prop="total_weight">
+                <div class="flex">
+                  <ElInputNumber 
+                    style="width:100%"  
+                    controls-position="right" 
+                    :precision="3" 
+                    v-model="data.ladder[0].total_weight" 
+                  />
+                  <span class="ml-[5px]">g</span>
+                </div>
+              </ElFormItem>
+            </ElCol>
+
+            <ElCol :span="6" >
+              <ElFormItem label="单位" prop="unit">
+                <UnitQuery v-model="data.ladder[0].unit" />
+              </ElFormItem>
+            </ElCol>
+
+            <ElCol :span="6">
+              <ElFormItem label="品牌" prop="brand_id">
+                <BrandQuey v-model="data.ladder[0].brand_id" />
+              </ElFormItem>
+            </ElCol>
+
+            <ElCol :span="12">
+              <ElFormItem label="商品分类" prop="cat_id">
+                <CategoryFlattenQuery
+                  ref="categoryFlattenQueryRef"
+                  :disabled="isUpdate"
+                  v-model="data.ladder[0].cat_id" 
+                  :companyNo="currentCompany.companyNo"
+                  @change="handleCategoryChange"
+                />
+              </ElFormItem>
+            </ElCol>
+
+            <ElCol :span="12">
+              <ElFormItem 
+                label="商品名称" 
+                prop="good_name">
+                <ElInput v-model="data.ladder[0].good_name"  placeholder='商品名称' />
+              </ElFormItem>
+              <!-- <SpecForm v-model:specs="data.specs" /> -->
+            </ElCol>
+          </ElRow>
+
+          <ElRow :gutter="10" v-if="data.ladder.length > 0">
+            <ElCol :span="12">
+              <ElFormItem label="工艺说明" prop="cost_desc">
+                <ElInput v-model="data.ladder[0].cost_desc" placeholder="工艺说明" />
+              </ElFormItem>
+              
+              <ElFormItem label="产品用途" prop="use_desc">
+                <ElInput v-model="data.ladder[0].use_desc" placeholder="产品用途" />
+              </ElFormItem>
+
+              <ElFormItem label="产品备注" prop="remark">
+                <ElInput v-model="data.ladder[0].remark" placeholder="产品用途" />
+              </ElFormItem>
+            </ElCol>      
+            
+            <ElCol :span="12">
+              <SpecForm v-model:specs="data.ladder[0].specinfo" />
+            </ElCol>
+          </ElRow>
+
+          <!-- 贵金属 -->
+          <ElRow v-if="data.ladder.length > 0 && isNoble">
+            <ElCol :span="12">
+              <ElFormItem label="配置要求" prop="config">
+                <ElSelect placeholder="配置要求" v-model="data.ladder[0].config" style="width: 100%">
+                  <ElOption 
+                    v-for="(item, index) in configOptions"
+                    :key="item + index"
+                    :label="item"
+                    :value="item"
+                  />
+                </ElSelect>
+              </ElFormItem>
+            </ElCol>
+          
+            <ElCol :span="6">
+              <ElFormItem label="金属克重" prop="specs_weight">
+                <UnitInput unit="g" v-model="data.ladder[0].specs_weight" />
+              </ElFormItem>
+            </ElCol>
+          
+            <ElCol :span="6">
+              <ElFormItem label="启用金价" prop="is_gold_price">
+                <ElSelect v-model="data.ladder[0].is_gold_price"  style="width: 100%">
+                  <ElOption 
+                    v-for="item in isGoldPriceOptions"
+                    :value="item.value"
+                    :label="item.label"
+                  />
+                </ElSelect>
+              </ElFormItem>
+            </ElCol>
+          </ElRow>
+
+          <ElRow v-if="data.ladder.length > 0 && isNoble">
+            <ElCol :span="16">
+              <ElFormItem label="其他配置要求" prop="other_config" label-width="110px">
+                <ElInput
+                  v-model="data.ladder[0].other_config"
+                  type="textarea" 
+                  placeholder="其他配置要求"
+                  maxlength="250"
+                  show-word-limit
+                  :rows="2"
+                />
+              </ElFormItem>
+            </ElCol>
+          
+            <ElCol :span="8">
+
+              <!-- -->
+              <ElFormItem label="金属种类" prop="metal_id">
+                <ElSelect placeholder="贵金属种类" style="width: 100%" v-model="data.ladder[0].metal_id">
+                  <ElOption 
+                    v-for="opt in nobleOptions" 
+                    :value="opt.value" 
+                    :label="opt.label" 
+                  />
+                </ElSelect>
+              </ElFormItem>
+            </ElCol>
+          </ElRow>
+
+          <ElRow v-if="data.ladder.length > 0">
+            <ElCol :span="8">
+              <ElFormItem label="商品图片" prop="good_img">
+                <div class="flex justify-start">
+                  <ImageSingleUpload v-model="data.ladder[0].good_img" />
+                  <p class="ml-[10px]">小于1Mb</p>
+                </div>
+              </ElFormItem>
+            </ElCol>
+
+
+            <ElCol :span="8">
+              <ElFormItem label="附件" prop="file_url">
+                <UploadFile 
+                  v-model="data.ladder[0].file_url" 
+                />
+              </ElFormItem>
+            </ElCol>
+
+            <ElCol :span="8">
+              <div class="w-full flex flex-col items-end justify-center">
+                <h3 class="text-right font-bold mb-[5px]">竞价单总额: {{ total }}</h3>
+                <ElButton type="primary" @click="submit">保 存</ElButton>
+              </div>
+            </ElCol>
+          </ElRow>
+        </template>
+
+        <ElCol :span="24" v-if="!isDetail && !isCopy && !isUpdate">
           <div class="flex justify-end">
-            <ElButton type="primary" @click="submit">保存</ElButton>
+            <ElButton type="primary" @click="submit">保 存</ElButton>
           </div>
         </ElCol>
-      </ElRow>
+      
     </ElForm>
 
     <ProjectModal 

+ 13 - 6
src/views/sellOut/zixunOrder/config/_options.ts

@@ -31,8 +31,8 @@ export const isCustomizedOptions = [
   { value: '1', label: '定制' }
 ]
 
-export const initialData = {
-  is_project: '', // 竞价单类型
+export const createInitialData = () => ({
+  is_project: '0', // 竞价单类型
   area: '',
   name: '', // 项目名称
   platform_code: '', // 所属平台
@@ -43,13 +43,13 @@ export const initialData = {
   endtime: '', // 竞价截止时间
   use_desc: '', // 竞价截止时间
   ladder: [] // 商品要求
-}
+})
 
 export const initialBidGoodData = {
   arrival_time: '',
-  budget_price: '',
-  is_addrs: '',
-  num: '',
+  budget_price: '0',
+  is_addrs: '0',
+  num: '0',
   brand_id: '',
   is_custom: '',
   total_weight: '',
@@ -100,6 +100,13 @@ export const rules =  {
       trigger: "change",
     },
   ],
+  ladder: [
+    {
+      required: true,
+      message: '请添加竞价商品要求',
+      trigger: 'change'
+    }
+  ]
 }
 
 

+ 93 - 4
src/views/sellOut/zixunOrder/detail.vue

@@ -1,24 +1,113 @@
 <script setup lang="ts">
+import { ref, h, computed, nextTick } from "vue";
+import { statusOptions } from "./config/_options";
+import BaseForm from "./components/baseForm.vue";
 import { useDetail } from "/@/hooks/core/useDetail";
-import BaseForm from "./components/baseForm.vue"
+import BasicDescriptions from "/@/components/BasicDescriptions";
+import { useRenderIcon } from "/@/components/ReIcon/src/hooks";
+import { useTask } from "/@/hooks/core"
+import { httpStatus } from "/@/api/sellOut/zixunOrder";
+import { useRoute, useRouter } from "vue-router"
+
+
+import { 
+  ElTag, 
+  ElButton, 
+  ElTooltip,
+  ElMessageBox
+} from "element-plus"
 
 const { 
   id, 
   title, 
-  isDetail, 
+  isDetail: _isDetail, 
   collapses 
 } = useDetail({ baseName: "竞价单", collapseLen: 5 });
 
 
+const route = useRoute()
+const router = useRouter()
+
+const isUpdate = computed(() => route.query.type == 'update')
+const isCopy = computed(() => route.query.type == 'copy')
+
+const isDetail = computed(() => {
+  return _isDetail.value && !isCopy.value && !isUpdate.value
+})
+
+
+const statusTask = useTask({ success(){ 
+  isDisplay.value = false 
+  nextTick(() => isDisplay.value = true)
+}})
+
+const isDisplay = ref(true)
+const detail = ref<Record<string, any>>({})
+const isPause = computed(() => detail.value.status === '1')
+
+function onClick(isPause){
+  const status = isPause ? '7' : '1'
+
+  ElMessageBox.confirm(
+    '是否确认' + ( isPause ? '停止招标任务?' : '重启招标任务?'), 
+    isPause ? '' : '重启需要重新编辑竞价信息',
+    {
+      confirmText: '确认',
+      cancelText: '取消',
+      type: 'warning'
+    }
+  ).then(async () => {
+    if (isPause) {
+      statusTask.run(httpStatus({ infoNo: id.value, status }))
+    } else {
+      router.push('/sellOut/zixunOrderDetail?id=' + id.value + '&type=update')
+    }
+  })
+}
 </script>
 
 <template>
   <div class="p-[10px] bg-white">
     <ElTabs>
-      <ElTabPane :label="title">
+      <ElTabPane :label="title" v-loading="statusTask.loading">
+        <BasicDescriptions
+          v-if="isDetail"
+          class="mb-[10px]"
+          :data="detail" 
+          :columns="[
+            { label: '竞价单编号', span: 6, field: 'infoNo' }, 
+            { label: '状态', span: 6, field: 'status', render(status){
+              return h('div', { style: 'display:flex; align-items: center' },[
+                h(ElTag, { size: 'small', type: 'primary' }, { default: statusOptions.find(item => item.value === status)?.label || '--'}),
+                h(ElTooltip, 
+                  { content: isPause ? '停止招标任务' : '重启招标任务', placement: 'top' }, 
+                  { 
+                    default: h(ElButton, { 
+                      link: true, 
+                      icon: useRenderIcon(isPause ? 'video-pause' : 'video-play'), 
+                      onClick: () => onClick(isPause) 
+                    })
+                  }
+                )
+              ])
+            }}, 
+            { label: '竞价次数', span: 3 , field: 'bargain_num' }, 
+            { label: '创建人', span: 3, field: 'salesman' }, 
+            {label: '创建时间', span: 6, field: 'addtime'}
+          ]" 
+        >
+      </BasicDescriptions>
+
         <ElCollapse v-model="collapses">
           <ElCollapseItem :title="title" name="1">
-            <BaseForm />
+            <BaseForm
+              v-if="isDisplay"
+              @getDetail="data => detail = data"
+              :isUpdate="isUpdate"
+              :isDetail="isDetail"
+              :isCopy="isCopy"
+              :id="id"
+            />
           </ElCollapseItem>
         </ElCollapse>
       </ElTabPane>

+ 20 - 3
src/views/sellOut/zixunOrder/index.vue

@@ -3,6 +3,7 @@ import { ref } from "vue";
 import searchConfig from "./config/search.config";
 import contentConfig from "./config/content.config";
 import { usePageSearch, type PageHooks, type PageEvents } from "/@/hooks/page";
+import { useRenderIcon } from "/@/components/ReIcon/src/hooks";
 import { useRouter } from "vue-router"
 
 const PageName = "zixunOrder";
@@ -15,10 +16,11 @@ const router = useRouter()
 const events: PageEvents = {
   content: {
     create: () => router.push('/sellOut/zixunOrderDetail'),
-    update: () => router.push('/sellOut/zixunOrderDetail?id=&type=update'),
-    preview: () => router.push('/sellOut/zixunOrderDetail?id=&type=preview')
+    preview: (row) => router.push('/sellOut/zixunOrderDetail?id=' + row.infoNo  + '&type=preview')
   }
 }
+
+const handleCopy = (infoNo) => router.push('/sellOut/zixunOrderDetail?id=' + infoNo  + '&type=copy')
 </script>
 
 <template>
@@ -29,6 +31,21 @@ const events: PageEvents = {
       :contentConfig="contentConfig"
       :search-config="searchConfig"
       :get-content-ref="ref => (instance = ref)"
-    />
+    >
+      <template #content_action="{ is_project, status, infoNo }">
+        <ElTooltip
+          v-if="(is_project == '0' || is_project == '2') && status != '0'" 
+          placement="top"
+          content="复制" 
+        >
+          <ElButton 
+            link 
+            type="primary"
+            :icon="useRenderIcon('ppt')"
+            @click="() => handleCopy(infoNo)"
+          />
+        </ElTooltip>
+      </template>
+    </PageContainer>
   </PageAuth>
 </template>