feat: 🚀 订阅功能

This commit is contained in:
2025-09-16 16:38:30 +08:00
parent eb1b66a066
commit d3a3ef2911
456 changed files with 40544 additions and 124 deletions

View File

@@ -0,0 +1,373 @@
<template>
<div class="search-box1" ref="searchRef">
<el-form
ref="ruleFormRef"
:model="_searchResult"
:inline="props.inline ? false : true"
class="form-box"
:show-message="false"
style="height: 100%; font-size: 14px"
>
<template v-for="item in formData" :key="item.prop">
<el-form-item
:label="item.label"
:prop="item.prop"
:label-width="labelWidth || '81px'"
:rules="item.rules"
:error="item.error"
:show-message="item.showMessage ? item.showMessage : false"
:inline-message="item.inlineMessage"
:style="item.style ? item.style : 'margin-right:8px;position: relative;'"
:required="item.required"
:class="item.class ? item.class : 'form-item'"
>
<template v-if="item.type === 'input'">
<el-input
v-model.trim="_searchResult[`${item.prop}`]"
:placeholder="item.placeholder"
:disabled="item.disabled"
:maxlength="item.maxLength ? item.maxLength : 255"
@input="valueVerify(item)"
>
</el-input>
</template>
<template v-if="item.type === 'inputs'">
<el-input
v-model.trim="_searchResult[`${item.startProp}`]"
:placeholder="item.startPlaceholder"
:disabled="item.startDisabled"
maxlength="255"
style="width: 144px !important"
@input="valueVerifyInputs(item)"
/>
<span style="margin: 0 3px">-</span>
<el-input
v-model.trim="_searchResult[`${item.endProp}`]"
:placeholder="item.endPlaceholder"
:disabled="item.endDisabled"
maxlength="255"
style="width: 144px !important"
@input="valueVerifyInputs(item)"
/>
</template>
<template v-if="item.type === 'select'">
<el-select
v-model="_searchResult[`${item.prop}`]"
:placeholder="item.placeholder"
clearable
:disabled="item.disabled"
>
<el-option
v-for="options in item.options"
:label="options.label"
:value="options.value"
:key="options.label"
/>
</el-select>
</template>
<template v-if="item.type === 'date'">
<el-date-picker
:disabled="item.disabled"
v-model="_searchResult[`${item.prop}`]"
:type="item.type"
:placeholder="item.placeholder"
format="YYYY-MM-DD"
:style="item.style"
value-format="YYYY-MM-DD"
/>
</template>
<template v-if="item.type === 'selectRemote' || item.type === 'selectRemoteUser'">
<el-select
v-model="_searchResult[`${item.prop}`]"
:placeholder="item.placeholder"
clearable
remote
reserve-keyword
filterable
ref="slectRef1"
class="m-2 select"
remote-show-suffix
:remote-method="
(query:any) => {
remoteMethod(
query,
item
);
}
"
:disabled="item.disabled"
>
<el-option
:label="option.label"
:value="option.value"
v-for="option in item.options"
:key="option.value"
/>
</el-select>
</template>
<!-- 多选带模糊搜索 -->
<template v-if="item.type === 'selectMultiple'">
<el-select
v-model="_searchResult[`${item.prop}`]"
multiple
filterable
@remove-tag="handleRomoveTag(item)"
:disabled="item.disabled"
:placeholder="item.placeholder"
>
<!-- 循环渲染选项label 为显示文本value 为实际提交值 -->
<el-option
v-for="option in item.options"
:key="option.value"
:label="option.label"
:value="option.value"
></el-option>
</el-select>
</template>
<template
v-if="item.type === 'selectMultipleRemoteCustomersNames' || item.type === 'selectProductLinesRemote'"
>
<el-select
v-model="_searchResult[`${item.prop}`]"
:placeholder="item.placeholder"
remote
multiple
filterable
class="m-2 select"
remote-show-suffix
@clear="handleSelectClear(item.prop)"
:remote-method="(query:any)=> handleSelectMultipleRemote(query, item)"
:disabled="item.disabled"
>
<el-option
:label="option.label"
:value="option.value"
v-for="option in item.options"
:key="option.label"
/>
</el-select>
</template>
</el-form-item>
</template>
</el-form>
</div>
</template>
<script lang="ts" setup name="Search">
import { ref } from "vue";
import { FormInstance } from "element-plus";
import { getCustomersApi, getUsersApi, getProductLinesApi } from "@/api/modules/global";
import { integerRexg, numberDecimalSeparatorRexg } from "@/utils/regexp/index";
const ruleFormRef = ref<FormInstance>();
const props = defineProps<{
formData: any[];
labelWidth?: string;
ruleForm: Record<string, any>;
style?: string;
inline?: Boolean;
getSearchValue?: () => void;
selectMultipleRemoveTag?: () => void;
setRuleFormValue?: () => void;
}>();
let _searchResult = computed(() => {
return props.ruleForm;
});
const emits = defineEmits<{
(e: "getSearchValue", result: Record<string, any>): void;
(e: "setMaterialList", result: Record<string, any>): void;
(e: "setRuleFormValue", result: Record<string, any>): void;
(e: "selectMultipleRemoveTag", result: Record<string, any>): void;
}>();
const handleRomoveTag = (item: any) => {
emits("selectMultipleRemoveTag", { item, org_number: _searchResult.value.org_number });
};
//客戶
const getCustomers = async (keywords: any, item: any) => {
let org_number = _searchResult.value.org_number.join(",");
const result: any = await getCustomersApi({ keywords, org_number });
if (result?.code === 0) {
const { data } = result;
if (Array.isArray(data) && data.length) {
let options: any = [];
data.forEach((item: any) => {
options.push({
value: item.customer_number,
label: item.customer_name
});
});
item.options = options;
}
}
};
//品线
const getProductLines = async (keywords: any, item: any) => {
const result: any = await getProductLinesApi({ keywords });
if (result?.code === 0) {
const { data } = result;
if (Array.isArray(data) && data.length) {
let options: any = [];
data.forEach((item: any) => {
options.push({
value: item,
label: item
});
});
item.options = options;
}
}
};
//订阅账号
const getUsers = async (keywords: any, item: any) => {
const result: any = await getUsersApi({ keywords });
if (result?.code === 0) {
const { data } = result;
if (Array.isArray(data) && data.length) {
let options: any = [];
data.forEach((item: any) => {
options.push({
value: item.dduid,
label: item.realname
});
});
item.options = options;
}
}
};
//远程搜索多选
const handleSelectMultipleRemote = (query: any, item: any) => {
if (!query) {
return;
}
let valClone = query.replace(/^\s*|\s*$/g, "");
if (!valClone) {
return;
}
//客户
if (item.type === "selectMultipleRemoteCustomersNames") {
getCustomers(valClone, item);
}
if (item.type === "selectProductLinesRemote") {
getProductLines(valClone, item);
}
};
const remoteMethod = async (query: any, item: any) => {
if (!query) {
return;
}
let valClone = query.replace(/^\s*|\s*$/g, "");
if (!valClone) {
return;
}
if (item.type === "selectRemoteUser") {
getUsers(valClone, item);
}
};
const handleSelectClear = (prop: any) => {
console.log("会触发吗?", prop);
};
//input输入验证
const valueVerify = (item: any) => {
//只能输入整数
if (item.reg === "integerRexg") {
let value = integerRexg(_searchResult.value[item.prop]);
_searchResult.value[item.prop] = value;
}
if (item.reg === "numberDecimalSeparatorRexg") {
let value = numberDecimalSeparatorRexg(_searchResult.value[item.prop]);
_searchResult.value[item.prop] = value;
}
emits("getSearchValue", {
val: _searchResult.value,
prop: item.prop
});
};
const valueVerifyInputs = (item: any) => {
//只能输入整数
if (item.reg === "integerRexg") {
let value = integerRexg(_searchResult.value[item.startProp]);
_searchResult.value[item.startProp] = value;
}
emits("getSearchValue", {
val: _searchResult.value,
prop: item.prop
});
};
// const handleSelectMultipleClear = (item: any) => {
// console.log(item, "===========>");
// };
</script>
<style lang="scss" scope>
.search-box1 {
position: relative;
display: flex;
min-width: 1280px;
padding: 16px;
background: #ffffff;
border-radius: 6px;
// 单据头用的样式
.form-box {
// width: 85%;
.form-item {
width: 392px !important;
// height: 32px;
// 原代码有 height: 32px !important; 这会导致子元素高度超出后被遮盖
height: auto !important; // 改为自动高度
min-height: 32px; // 保留最小高度,未选择时对齐
margin-bottom: 8px !important;
.el-form-item__label {
font-size: 12px !important;
}
.el-select {
width: 392px;
}
.el-form-item--default {
width: 392px;
}
}
.form-item1 {
width: 594px !important;
// height: 32px;
// 原代码有 height: 32px !important; 这会导致子元素高度超出后被遮盖
height: auto !important; // 改为自动高度
min-height: 32px; // 保留最小高度,未选择时对齐
margin-bottom: 8px !important;
.el-form-item__label {
font-size: 12px !important;
}
.el-select {
width: 594px;
}
.el-form-item--default {
width: 594px;
}
}
.form-item2 {
width: 494px !important;
}
margin-bottom: 8px !important;
}
}
.el-form-item--default .el-form-item__label {
height: 32px;
margin-bottom: 8px;
// font-size: 12px;
line-height: 32px;
color: rgb(92 92 92 / 100%);
}
</style>

View File

@@ -0,0 +1,58 @@
export interface formDataIProps {
reg?: any;
prop?: string;
label?: string;
isShow?: boolean;
maxLength?: any;
value?: any; //默认值
bomList?: any;
placeholder?: string;
labelWidth?: string;
class?: string;
urlType?: string;
rules?: any;
error?: string;
showMessage?: boolean;
inlineMessage?: boolean;
size?: string;
style?: string;
autosize?: any;
inputWidth?: string;
brIndex?: number;
endDisabled?: any;
endProp?: any;
optionProps?: any;
startDisabled?: any;
startProp?: any;
rgx?: any;
isTxt?: any;
type:
| "input"
| "textarea"
| "select"
| "datetimepicker"
| "date"
| "daterange"
| "radio"
| "checked"
| "cascader"
| "selectRemote"
| "selectRemote1"
| "selectRemote2"
| "selectRemote3"
| "inputs"
| "selectMultiple"
| "customCondition";
options?: any;
startPlaceholder?: string;
endPlaceholder?: string;
defaultTime?: any;
radios?: any;
checkeds?: any;
isCopy?: boolean;
required?: boolean;
disabled?: boolean;
formClass?: string;
isNumber?: boolean;
ruleForm?: any;
}