snow 9 months ago
parent
commit
0216d4a064

BIN
dist-zip/build.zip


+ 33 - 17
src/components/BasicForm/src/basic-form.vue

@@ -1,6 +1,6 @@
 <script setup lang="ts">
 import { onMounted, ref } from "vue";
-import { ElForm } from "element-plus";
+import { ElForm, ElMessage } from "element-plus";
 import { useVModel } from "@vueuse/core";
 import { basicFormProps } from "./types";
 import { CONTACT_OPTIONS } from "./configs/config";
@@ -34,9 +34,25 @@ onMounted(() => {
     CONTACT_OPTIONS.find(c => c.rule.validator("", data))?.value || "4";
 });
 
-function handleChange({ trigger, onChange, field }: any) {
-  if (!trigger || trigger !== "change") {
-    return;
+function handleChange({ onChange, field }: any, isRange: boolean) {
+  // if (!trigger || trigger !== "change") {
+  //   return;
+  // }
+  if(isRange){
+    const [first, end] = formData.value[field]
+    if(!first && !end){
+      return;
+    }
+
+    if(!first){
+      ElMessage.warning('请输入起始值')
+      return;
+    }
+
+    if(!end){
+      ElMessage.warning('请输入结束值')
+      return;
+    }
   }
 
   emit("trigger-blur", formData.value);
@@ -65,32 +81,32 @@ defineExpose({
             <!-- 输入框 -->
             <template v-if="item.type === 'input'">
               <el-input v-model="formData[item.field]" :placeholder="item.placeholder" v-bind="item.otherOptions"
-                clearable />
+                clearable  @change="handleChange(item)" />
             </template>
 
             <!-- 数字输入 -->
             <template v-if="item.type === 'number'">
-              <el-input-number v-model="formData[item.field]" :placeholder="item.placeholder" clearable />
+              <el-input-number v-model="formData[item.field]" :placeholder="item.placeholder" clearable  @change="handleChange(item)" />
             </template>
 
             <!-- 选择框 -->
             <template v-if="item.type === 'select'">
-              <el-select v-model="formData[item.field]" :placeholder="item.placeholder" clearable>
+              <el-select v-model="formData[item.field]" :placeholder="item.placeholder" clearable  @change="handleChange(item)">
                 <el-option v-for="(opt, index) in item.options" :key="index" :label="opt.label" :value="opt.value"
                   :disabled="item.otherOptions?.disabled ? opt.disabled : false" />
               </el-select>
             </template>
             <template v-if="item.type === 'radio'">
-              <el-radio-group v-model="formData[item.field]">
+              <el-radio-group v-model="formData[item.field]"  @change="handleChange(item)">
                 <el-radio v-for="(opt, index) in item.options" :key="index" :label="opt.value">{{ opt.label
                   }}</el-radio>
               </el-radio-group>
             </template>
 
             <template v-if="item.type === 'input_group'">
-              <el-input v-model="formData[item.field]">
+              <el-input v-model="formData[item.field]"  @change="handleChange(item)">
                 <template #prepend>
-                  <el-select v-model="formData[item.type_field]" placeholder="Select" style="width: 150px">
+                  <el-select v-model="formData[item.type_field]" placeholder="Select" style="width: 150px"  @change="handleChange(item)">
                     <el-option v-for="option in item.otherOptions?.inputGroupOptions" :value="option.value"
                       :label="option.label" :key="option.value" />
                   </el-select>
@@ -103,14 +119,14 @@ defineExpose({
             </template>
 
             <template v-if="item.type === 'checkbox'">
-              <el-checkbox-group v-model="formData[item.field]">
+              <el-checkbox-group v-model="formData[item.field]"  @change="handleChange(item)">
                 <el-checkbox v-for="(opt, index) in item.options" :key="index" :label="opt.value">{{ opt.label }}
                 </el-checkbox>
               </el-checkbox-group>
             </template>
 
             <template v-if="item.type === 'date_picker'">
-              <el-date-picker v-model="formData[item.field]" @change="handleChange(item)" style="width: 100%"
+              <el-date-picker v-model="formData[item.field]" @change="handleChange(item)" style="width: 100%" 
                 format="YYYY-MM-DD" v-bind="item.otherOptions" />
             </template>
 
@@ -118,7 +134,7 @@ defineExpose({
             <template v-if="item.type === 'contact'">
               <el-input v-model="formData[item.field]">
                 <template #prepend>
-                  <el-select style="width: 115px" v-model="contact">
+                  <el-select style="width: 115px" v-model="contact"> 
                     <el-option v-for="c in CONTACT_OPTIONS" :key="c.value" :label="c.label" :value="c.value" />
                   </el-select>
                 </template>
@@ -133,20 +149,20 @@ defineExpose({
             </template>
 
             <template v-if="item.type === 'supplier-query'">
-              <SupplierQuery v-model="formData[item.field]" :placeholder="item.placeholder || '卖出方公司'" />
+              <SupplierQuery v-model="formData[item.field]" :placeholder="item.placeholder || '卖出方公司'"  @change="handleChange(item, true)" />
             </template>
 
             <template v-if="item.type === 'business-query'">
-              <BusinessQuery v-model="formData[item.field]" :placeholder="item.placeholder || '买入方公司'" />
+              <BusinessQuery v-model="formData[item.field]" :placeholder="item.placeholder || '买入方公司'"  @change="handleChange(item, true)" />
             </template>
 
             <template v-if="item.type === 'range'">
               <div flex gap-2 w-full>
-                <el-input v-model="formData[item.field][0]" :placeholder="item.otherOptions?.startPlaceholder" />
+                <el-input v-model="formData[item.field][0]" :placeholder="item.otherOptions?.startPlaceholder" @change="handleChange(item, true)" />
 
                 <span>至</span>
 
-                <el-input v-model="formData[item.field][1]" :placeholder="item.otherOptions?.endPlaceholder" />
+                <el-input v-model="formData[item.field][1]" :placeholder="item.otherOptions?.endPlaceholder" @change="handleChange(item, true)" />
               </div>
             </template>
           </el-form-item>

+ 4 - 0
src/components/BasicForm/src/fields/business-query.vue

@@ -9,6 +9,8 @@ const props = defineProps<{
   disabled?: boolean;
 }>();
 
+const emits = defineEmits(['change']);
+
 const value = useVModel(props, "modelValue");
 
 const RemoteSelectRef = ref<InstanceType<typeof RemoteSelect> | null>(null);
@@ -32,5 +34,7 @@ defineExpose({
     request-prop="company_name"
     response-label-prop="company_name"
     response-val-prop="companyNo"
+    @item-change="emits('change')"
+    @clear="emits('change')"
   />
 </template>

+ 4 - 0
src/components/BasicForm/src/fields/supplier-query.vue

@@ -8,6 +8,8 @@ const props = defineProps<{
   modelValue?: string;
 }>();
 
+const emits = defineEmits(['change']);
+
 const value = useVModel(props, "modelValue");
 const RemoteSelectRef = ref<InstanceType<typeof RemoteSelect> | null>(null);
 
@@ -29,5 +31,7 @@ defineExpose({
     requset-prop="name"
     response-label-prop="name"
     response-val-prop="code"
+    @item-change="emits('change')"
+    @clear="emits('change')"
   />
 </template>

+ 2 - 2
src/components/PageSearch/src/page-search.vue

@@ -75,8 +75,8 @@ if (searchParams.basic) {
 
       <template #action>
         <div style="width: 100%" flex gap-2>
-          <el-button type="primary" :icon="useRenderIcon('search')" @click="handleSearchClick">
-            搜索
+          <el-button type="primary" :icon="useRenderIcon('refresh-right')" @click="handleSearchClick">
+            刷新
           </el-button>
 
           <el-button :icon="useRenderIcon('refresh')" @click="handleResetClick">

+ 68 - 23
src/components/ReImageVerify/src/hooks.ts

@@ -8,6 +8,7 @@ import { ref, onMounted } from "vue";
 export const useImageVerify = (width = 120, height = 40) => {
   const domRef = ref<HTMLCanvasElement>();
   const imgCode = ref("");
+  const password = ref(0)
 
   function setImgCode(code: string) {
     imgCode.value = code;
@@ -15,9 +16,12 @@ export const useImageVerify = (width = 120, height = 40) => {
 
   function getImgCode() {
     if (!domRef.value) return;
-    imgCode.value = draw(domRef.value, width, height);
+    const code = draw(domRef.value, width, height);
+    password.value = code.password
+    imgCode.value = code.imgCode
   }
 
+
   onMounted(() => {
     getImgCode();
   });
@@ -25,6 +29,7 @@ export const useImageVerify = (width = 120, height = 40) => {
   return {
     domRef,
     imgCode,
+    password,
     setImgCode,
     getImgCode
   };
@@ -35,6 +40,34 @@ function randomNum(min: number, max: number) {
   return num;
 }
 
+function generateArithmeticCaptcha() {
+  var firstNumber = Math.floor(Math.random() * 10)
+  var secondNumber = Math.floor(Math.random() * 10)
+  var operator = Math.random() < 0.5 ? '+' : (Math.random() < 0.5 ? '-' : '*')
+  var result
+
+  switch (operator) {
+    case '+':
+      result = firstNumber + secondNumber
+      break
+    case '-':
+      result = firstNumber - secondNumber
+      break
+    case '*':
+      result = firstNumber * secondNumber
+      break
+  }
+
+  if (Number(result) < 0) {
+    return generateArithmeticCaptcha()
+  }
+
+  return {
+    subject: (`${firstNumber}${operator === '*' ? '×' : operator}${secondNumber}`).trim(),
+    answer: result
+  }
+}
+
 function randomColor(min: number, max: number) {
   const r = randomNum(min, max);
   const g = randomNum(min, max);
@@ -42,18 +75,27 @@ function randomColor(min: number, max: number) {
   return `rgb(${r},${g},${b})`;
 }
 
+const map = {
+  '+': '加',
+  '-': '减',
+  '×': '乘'
+}
+
 function draw(dom: HTMLCanvasElement, width: number, height: number) {
   let imgCode = "";
-
+  let password = 0
   const NUMBER_STRING = "0123456789";
-
   const ctx = dom.getContext("2d");
-  if (!ctx) return imgCode;
-
+  if (!ctx) return {
+    imgCode,
+    password: 0
+  };
   ctx.fillStyle = randomColor(180, 230);
   ctx.fillRect(0, 0, width, height);
-  for (let i = 0; i < 4; i += 1) {
-    const text = NUMBER_STRING[randomNum(0, NUMBER_STRING.length)];
+  const code = generateArithmeticCaptcha();
+  password = code.answer
+  for (let i = 0; i < code.subject.length; i += 1) {
+    const text = map[code.subject[i]] || code.subject[i];
     imgCode += text;
     const fontSize = randomNum(18, 41);
     const deg = randomNum(-30, 30);
@@ -66,20 +108,23 @@ function draw(dom: HTMLCanvasElement, width: number, height: number) {
     ctx.fillText(text, -15 + 5, -15);
     ctx.restore();
   }
-  for (let i = 0; i < 5; i += 1) {
-    ctx.beginPath();
-    ctx.moveTo(randomNum(0, width), randomNum(0, height));
-    ctx.lineTo(randomNum(0, width), randomNum(0, height));
-    ctx.strokeStyle = randomColor(180, 230);
-    ctx.closePath();
-    ctx.stroke();
-  }
-  for (let i = 0; i < 41; i += 1) {
-    ctx.beginPath();
-    ctx.arc(randomNum(0, width), randomNum(0, height), 1, 0, 2 * Math.PI);
-    ctx.closePath();
-    ctx.fillStyle = randomColor(150, 200);
-    ctx.fill();
-  }
-  return imgCode;
+  // for (let i = 0; i < 5; i += 1) {
+  //   ctx.beginPath();
+  //   ctx.moveTo(randomNum(0, width), randomNum(0, height));
+  //   ctx.lineTo(randomNum(0, width), randomNum(0, height));
+  //   ctx.strokeStyle = randomColor(180, 230);
+  //   ctx.closePath();
+  //   ctx.stroke();
+  // }
+  // for (let i = 0; i < 41; i += 1) {
+  //   ctx.beginPath();
+  //   ctx.arc(randomNum(0, width), randomNum(0, height), 1, 0, 2 * Math.PI);
+  //   ctx.closePath();
+  //   ctx.fillStyle = randomColor(150, 200);
+  //   ctx.fill();
+  // }
+  return {
+    imgCode,
+    password
+  };
 }

+ 9 - 2
src/components/ReImageVerify/src/index.vue

@@ -8,19 +8,22 @@ defineOptions({
 
 interface Props {
   code?: string;
+  password?: string;
 }
 
 interface Emits {
   (e: "update:code", code: string): void;
+  (e: "update:password", password: string): void;
 }
 
 const props = withDefaults(defineProps<Props>(), {
-  code: ""
+  code: "",
+  password: ""
 });
 
 const emit = defineEmits<Emits>();
 
-const { domRef, imgCode, setImgCode, getImgCode } = useImageVerify();
+const { domRef, imgCode, password, setImgCode, getImgCode } = useImageVerify();
 
 watch(
   () => props.code,
@@ -32,6 +35,10 @@ watch(imgCode, newValue => {
   emit("update:code", newValue);
 });
 
+watch(password, newValue => {
+  emit("update:password", newValue);
+});
+
 defineExpose({ getImgCode });
 </script>
 

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

@@ -6,7 +6,7 @@ import { useResponseHandle } from "/@/hooks";
 const RemoteSelect = defineComponent({
   name: "RemoteSelect",
   props: RemoteSelectProps,
-  emits: ["listChange", "itemChange", "itenDetail", "update:value", "inital"],
+  emits: ["listChange", "itemChange", "itenDetail", "update:value", "inital", "clear"],
   setup(props, { emit, expose }) {
     const value = computed({
       get() {

+ 1 - 0
src/views/InvoiceSales/capitalClaim/config/search.config.ts

@@ -6,6 +6,7 @@ const searchFormConfig: FormConfig = {
     {
       field: "create_timer",
       type: "date_picker",
+      trigger: 'change',
       otherOptions: {
         type: "daterange",
         startProp: "start",

+ 22 - 0
src/views/InvoiceSales/capitalClaim/index.vue

@@ -18,6 +18,9 @@ import { CAPITAL_STATUS } from "/@/utils/details/tragelog";
 import { httpWithdraw } from "/@/api/InvoiceSales/capitalClaim";
 import { httpRequsetExport } from "/@/utils/export";
 
+import { ElMessage } from "element-plus"
+import dayjs from "dayjs"
+
 import {
   xs_order_source_options,
   xs_order_type_options
@@ -92,6 +95,25 @@ async function onDownloadOpenInv() {
 
 
 async function onDownloadCapitalInfo() {
+  const params = pageContentRef.value.getBasicParams()
+  console.log(params)
+
+  if (!params.start || !params.end) {
+    ElMessage.warning('请选择导出的时间区间')
+    return
+  }
+
+  const start = dayjs(params.start);
+  const end = dayjs(params.end);
+  const diffDay = start.diff(end, 'days')
+  const startDays = start.daysInMonth();
+  const endDays = end.daysInMonth();
+
+  if (Math.abs(diffDay) > startDays - 1) {
+    ElMessage.warning('导出的时间区间不能超过一个月')
+    return;
+  }
+
   await httpRequsetExport({
     url: "orderPay/exportTrade",
     name: "资金认领数据表",

+ 50 - 2
src/views/login/index.vue

@@ -1,7 +1,6 @@
 <script setup lang="ts">
 import Motion from "./utils/motion";
 import { useRouter } from "vue-router";
-import { loginRules } from "./utils/rule";
 import type { FormInstance } from "element-plus";
 import { ElMessage } from "element-plus";
 import { storageSession } from "@pureadmin/utils";
@@ -14,6 +13,9 @@ import { useRenderIcon } from "/@/components/ReIcon/src/hooks";
 import { initRouter } from "/@/router/utils";
 import { loadEnv } from "@build/index";
 import logo from "/@/assets/logo.png";
+import { REGEXP_PWD } from "./utils/rule"
+import { isMobile } from "/@/utils/validate";
+
 
 defineOptions({
   name: "Login"
@@ -27,6 +29,7 @@ const router = useRouter();
 const loading = ref(false);
 const checked = ref(false);
 const ruleFormRef = ref<FormInstance>();
+const codePassword = ref("")
 const currentPage = computed(() => {
   return useUserStoreHook().currentPage;
 });
@@ -87,6 +90,51 @@ const onLogin = async (formEl: FormInstance | undefined) => {
   });
 };
 
+const loginRules = reactive(<FormRules>{
+  username: [
+    {
+      validator(_, value, callback) {
+        if (!value) {
+          callback(new Error("请输入手机号"));
+        } else if (!isMobile(value)) {
+          callback(new Error("手机号格式不正确"));
+        } else {
+          callback();
+        }
+      },
+      trigger: "blur"
+    }
+  ],
+  password: [
+    {
+      validator: (rule, value, callback) => {
+        if (value === "") {
+          callback(new Error("请输入密码"));
+        } else if (!REGEXP_PWD.test(value)) {
+          callback(new Error("密码格式应为6-18位数字、字母的组合"));
+        } else {
+          callback();
+        }
+      },
+      trigger: "blur"
+    }
+  ],
+  verifyCode: [
+    {
+      validator: (rule, value, callback) => {
+        if (value === "") {
+          callback(new Error("请输入验证码"));
+        } else if (String(codePassword.value) !== String(value)) {
+          callback(new Error("请输入正确的验证码"));
+        } else {
+          callback();
+        }
+      },
+      trigger: "blur"
+    }
+  ]
+});
+
 function toWindowPath() {
   window.open("https://beian.miit.gov.cn/");
 }
@@ -148,7 +196,7 @@ watch(ruleForm, () => {
               <el-input clearable v-model="ruleForm.verifyCode" placeholder="验证码" :prefix-icon="useRenderIcon('ri:shield-keyhole-line', { online: true })
                 ">
                 <template v-slot:append>
-                  <ReImageVerify v-model:code="imgCode" />
+                  <ReImageVerify v-model:code="imgCode" v-model:password="codePassword" />
                 </template>
               </el-input>
             </el-form-item>

+ 1 - 1
src/views/purchase/orderRecord/index.vue

@@ -151,7 +151,7 @@ function onDownloadTemplate() {
           <ElButton
             v-if="
               (String(row.status) === '1' || String(row.status) === '2') &&
-              hasPermissionWithCode('006') && String(row.is_comon) !== '1'
+              hasPermissionWithCode('022') && String(row.is_comon) !== '1'
             "
             type="primary"
             link

+ 1 - 1
src/views/supply/orderRecord/index.vue

@@ -148,7 +148,7 @@ const handleRevoke = (props) => {
           <ElButton
             v-if="
               (String(row.status) === '1' || String(row.status) === '2') &&
-              hasPermissionWithCode('006') && String(row.is_comon) === '0'
+              hasPermissionWithCode('022') && String(row.is_comon) === '0'
             "
             type="primary"
             link

+ 5 - 0
src/views/supply/porder/config/content.config.ts

@@ -34,6 +34,11 @@ const columns = [
     prop: "cxCode",
     width: "150px"
   },
+  {
+    label: "对账编号",
+    prop: "payNo",
+    width: "150px"
+  },
   {
     label: "销售订单编码",
     prop: "qrdCode",

+ 1 - 0
src/views/supply/porder/config/search.config.ts

@@ -19,6 +19,7 @@ const searchFormConfig: FormConfig = {
         inputGroupOptions: [
           { value: "sequenceNo", label: "采购单编码" },
           { value: "cxCode", label: "采购主单编码" },
+          { value: "payNo", label: "对账编号" },
           { value: "qrdCode", label: "销售订单编码" },
           { value: "goodNo", label: "商品编码" },
           { value: "goodName", label: "商品名称" },