504 lines
18 KiB
Vue
504 lines
18 KiB
Vue
<template>
|
||
<el-drawer v-model="drawerVisible" :destroy-on-close="true" size="720" :title="`${drawerProps.title}`">
|
||
<el-form
|
||
ref="ruleFormRef"
|
||
label-width="90px"
|
||
label-suffix=" :"
|
||
:rules="rules"
|
||
:disabled="drawerProps.isView"
|
||
:model="drawerProps.row"
|
||
:hide-required-asterisk="drawerProps.isView"
|
||
>
|
||
<el-form-item v-for="e in formColumns" :key="e.label" :label="e.label" :prop="e.prop" :required="e.required">
|
||
<template v-if="e.type === 'switch'">
|
||
<el-switch
|
||
v-model="e.value"
|
||
:active-value="`${1}`"
|
||
:inactive-value="`${0}`"
|
||
active-text="上架"
|
||
inactive-text="下架"
|
||
/>
|
||
</template>
|
||
<template v-if="e.type === 'radio'">
|
||
<el-radio-group v-model="e.value" class="ml-4">
|
||
<el-radio v-for="item in e.options" :key="item.value" :value="item.value" size="large">{{
|
||
item.label
|
||
}}</el-radio>
|
||
</el-radio-group>
|
||
</template>
|
||
<template v-if="e.type === 'select'">
|
||
<el-select filterable multiple v-model="e.value" :placeholder="`请选择${e.label}`" clearable>
|
||
<el-option
|
||
v-for="(item, index) in e.options"
|
||
:key="index"
|
||
:label="getField(item, e.fieldNames?.label)"
|
||
:value="getField(item, e.fieldNames?.value)"
|
||
/>
|
||
</el-select>
|
||
</template>
|
||
<template v-if="e.type === 'text'">
|
||
<el-input v-model="e.value" :placeholder="`请输入${e.label}`" clearable></el-input>
|
||
</template>
|
||
<template v-if="e.type === 'text-i'">
|
||
<el-input v-model="e.value" :placeholder="`请输入${e.label}`" clearable></el-input>
|
||
</template>
|
||
<template v-if="e.type === 'text-num'">
|
||
<el-input v-model="e.value" type="number" :placeholder="`请输入${e.label}`" clearable></el-input>
|
||
</template>
|
||
<template v-if="e.type === 'textarea-i'">
|
||
<el-input
|
||
|
||
v-model="e.value"
|
||
maxlength="36500"
|
||
:autosize="{ minRows: 8, maxRows: 9 }"
|
||
:placeholder="`请输入${e.label}`"
|
||
show-word-limit
|
||
/>
|
||
<div style="width: 100%">{{ e.describe }}</div>
|
||
</template>
|
||
<template v-if="e.type === 'textarea'">
|
||
<el-input
|
||
type="textarea"
|
||
v-model="e.value"
|
||
maxlength="3650"
|
||
:placeholder="`请输入${e.label}`"
|
||
show-word-limit
|
||
/>
|
||
</template>
|
||
<template v-if="e.type === 'tag'">
|
||
<div class="tag-more-s">
|
||
<el-input
|
||
v-model="drawerProps.row.searchText"
|
||
placeholder="请输入搜索内容"
|
||
clearable
|
||
@input="handleSearch"
|
||
/>
|
||
<div>
|
||
<span>选中标签:</span>
|
||
<div class="tag-more-h">
|
||
<el-checkbox-group v-model="TagList" @change="placeableCheckedCitiesChange">
|
||
<el-checkbox-button
|
||
class="vip-power"
|
||
v-for="item in matchingObjects"
|
||
:value="item.t_id"
|
||
:key="item.t_id"
|
||
>
|
||
{{ item.t_name }}
|
||
</el-checkbox-button>
|
||
</el-checkbox-group>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<span>未选中标签:</span>
|
||
<div class="tag-more-h">
|
||
<el-checkbox-group v-model="TagList" @change="placeableCheckedCitiesChange">
|
||
<el-checkbox-button
|
||
class="vip-power"
|
||
v-for="item in nonMatchingObjects"
|
||
:value="item.t_id"
|
||
:key="item.t_id"
|
||
>
|
||
{{ item.t_name }}
|
||
</el-checkbox-button>
|
||
</el-checkbox-group>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<template v-if="e.type === 'text-i'">
|
||
<UploadImg disabled v-model:image-url="drawerProps.row!.at_cover" width="135px" height="135px" :file-size="3">
|
||
<template #empty>
|
||
<el-icon><Avatar /></el-icon>
|
||
<span>请上传封面</span>
|
||
</template>
|
||
</UploadImg>
|
||
</template>
|
||
<template v-if="e.type === 'textarea-i'">
|
||
<div :class="{ 'img-more-h': fileList.length > 0 }">
|
||
<UploadImgs disabled v-model:file-list="fileList" :drag="false" border-radius="50%">
|
||
<template #empty>
|
||
<el-icon><Picture /></el-icon>
|
||
<span>请上传照片</span>
|
||
</template>
|
||
</UploadImgs>
|
||
</div>
|
||
</template>
|
||
</el-form-item>
|
||
</el-form>
|
||
<template #footer>
|
||
<el-button @click="drawerVisible = false">取消</el-button>
|
||
<el-button v-show="!drawerProps.isView" type="primary" @click="handleSubmit">确定</el-button>
|
||
</template>
|
||
</el-drawer>
|
||
</template>
|
||
|
||
<script setup lang="ts" name="UserDrawer">
|
||
import { ref, computed, reactive } from "vue";
|
||
import { ElMessage, FormInstance } from "element-plus";
|
||
import { AtlasList } from "@/api/interface/atlas/list";
|
||
import UploadImgs from "@/components/Upload/Imgs.vue";
|
||
import UploadImg from "@/components/Upload/Img.vue";
|
||
import { getTypeList } from "@/api/modules/tag/list";
|
||
import { extractURI } from "@/utils/eleValidate";
|
||
import { getAllList } from "@/api/modules/site/list";
|
||
type Options = {
|
||
label: string;
|
||
value: string | number;
|
||
};
|
||
interface Item {
|
||
t_id: any;
|
||
uri: any;
|
||
}
|
||
const getField = (item: any, fieldName: string | undefined) => {
|
||
return fieldName ? item[fieldName] : "";
|
||
};
|
||
type Fields = {
|
||
label: string;
|
||
prop: string;
|
||
type: string;
|
||
value: any;
|
||
required: boolean;
|
||
options?: Options[];
|
||
fieldNames?: { label: string; value: string };
|
||
describe?: string;
|
||
};
|
||
const generateRules = (fields: Fields[]) => {
|
||
return fields.reduce(
|
||
(acc, field) => {
|
||
const { type, required, label, prop } = field;
|
||
let message = "";
|
||
if (type === "select") {
|
||
message = `请选择${label}`;
|
||
}
|
||
if (type === "tag") {
|
||
message = `请选择${label}`;
|
||
}
|
||
if (type === "radio") {
|
||
message = `请选择${label}`;
|
||
}
|
||
if (type === "text") {
|
||
message = `请输入${label}`;
|
||
}
|
||
if (type === "text-num") {
|
||
message = `请输入${label}`;
|
||
}
|
||
if (type === "text-i") {
|
||
message = `请输入${label}`;
|
||
}
|
||
if (type === "textarea-i") {
|
||
message = `请输入${label}`;
|
||
}
|
||
if (type === "image") {
|
||
message = `请上传${label}`;
|
||
}
|
||
if (type === "image-more") {
|
||
message = `请上传${label}`;
|
||
}
|
||
acc[prop] = [{ required, message }];
|
||
return acc;
|
||
},
|
||
{} as Record<string, any>
|
||
);
|
||
};
|
||
|
||
const fileList = ref([] as any);
|
||
const TagList = ref([] as any);
|
||
const getTagList = async () => {
|
||
try {
|
||
const initParam = reactive({
|
||
t_type: 2,
|
||
limit: 3650,
|
||
page: 1,
|
||
t_hidden: 1
|
||
});
|
||
const siteRes = await getTypeList(initParam);
|
||
arrTagList.value = siteRes.data.item;
|
||
arrNewTagList.value = siteRes.data.item;
|
||
matchingObjects.value = [];
|
||
matchingObjectsA.value = [];
|
||
// 遍历第一个数组的每个元素
|
||
TagList.value.forEach(index => {
|
||
// 遍历第二个数组的每个元素
|
||
arrNewTagList.value.forEach(obj => {
|
||
// 如果第二个数组中的元素的t_id与第一个数组中的元素相同,则将其添加到新数组中
|
||
if (obj.t_id === index) {
|
||
matchingObjects.value = [...matchingObjects.value, obj];
|
||
matchingObjectsA.value = [...matchingObjectsA.value, obj];
|
||
const matchingIds = matchingObjectsA.value.map(obj => obj.t_id);
|
||
TagList.value = matchingIds;
|
||
}
|
||
});
|
||
});
|
||
// 创建一个新数组,包含objectArray中id不存在于arrayA的元素
|
||
nonMatchingObjects.value = arrNewTagList.value.filter(obj => !TagList.value.includes(obj.t_id));
|
||
nonMatchingObjectsB.value = arrNewTagList.value.filter(obj => !TagList.value.includes(obj.t_id));
|
||
} catch (error) {
|
||
console.log(error);
|
||
}
|
||
};
|
||
|
||
const getSiteList = ref([] as any);
|
||
getAllList().then(res => {
|
||
getSiteList.value = res.data.item;
|
||
});
|
||
|
||
const arrTagList = ref([] as any);
|
||
const arrNewTagList = ref([] as any);
|
||
const matchingObjects = ref([] as any); //选中相同
|
||
const nonMatchingObjects = ref([] as any); //没有选中的
|
||
const matchingObjectsA = ref([] as any); //选中相同
|
||
const nonMatchingObjectsB = ref([] as any); //没有选中的
|
||
|
||
// 处理搜索事件
|
||
function handleSearch() {
|
||
// 在输入事件中实时更新过滤后的标签列表
|
||
filteredTagList();
|
||
}
|
||
function filteredTagList() {
|
||
if (drawerProps.value.row!.searchText.length > 0) {
|
||
let arrData = arrTagList.value.filter(tag =>
|
||
tag.t_name.toLowerCase().includes(drawerProps.value.row!.searchText.toLowerCase())
|
||
);
|
||
if (arrData.length > 0) {
|
||
matchingObjects.value = matchingObjects.value.filter(objA => arrData.some(objB => objB.t_id === objA.t_id));
|
||
nonMatchingObjects.value = nonMatchingObjects.value.filter(objA => arrData.some(objB => objB.t_id === objA.t_id));
|
||
} else {
|
||
matchingObjects.value = [];
|
||
nonMatchingObjects.value = [];
|
||
}
|
||
} else {
|
||
matchingObjects.value = matchingObjectsA.value;
|
||
nonMatchingObjects.value = nonMatchingObjectsB.value;
|
||
}
|
||
}
|
||
|
||
//标签数组
|
||
async function placeableCheckedCitiesChange(value) {
|
||
matchingObjects.value = [];
|
||
matchingObjectsA.value = [];
|
||
drawerProps.value.row.at_tag = value;
|
||
// 遍历第一个数组的每个元素
|
||
TagList.value.forEach(index => {
|
||
// 遍历第二个数组的每个元素
|
||
arrNewTagList.value.forEach(obj => {
|
||
// 如果第二个数组中的元素的t_id与第一个数组中的元素相同,则将其添加到新数组中
|
||
if (obj.t_id === index) {
|
||
matchingObjects.value = [...matchingObjects.value, obj];
|
||
matchingObjectsA.value = [...matchingObjectsA.value, obj];
|
||
const matchingIds = matchingObjectsA.value.map(obj => obj.t_id);
|
||
TagList.value = matchingIds;
|
||
}
|
||
});
|
||
});
|
||
nonMatchingObjects.value = arrNewTagList.value.filter(obj => !TagList.value.includes(obj.t_id));
|
||
nonMatchingObjectsB.value = arrNewTagList.value.filter(obj => !TagList.value.includes(obj.t_id));
|
||
}
|
||
const formColumns = computed((): Fields[] => {
|
||
let newColumns = [] as any;
|
||
let columns = [
|
||
{
|
||
label: "图册名称",
|
||
prop: "at_name",
|
||
type: "text",
|
||
get value() {
|
||
return drawerProps.value.row!.at_name;
|
||
},
|
||
set value(val) {
|
||
drawerProps.value.row!.at_name = val;
|
||
},
|
||
required: true
|
||
},
|
||
{
|
||
label: "图册状态",
|
||
prop: "at_status",
|
||
type: "switch",
|
||
get value() {
|
||
return String(drawerProps.value.row!.at_status);
|
||
},
|
||
set value(val) {
|
||
drawerProps.value.row!.at_status = val;
|
||
},
|
||
required: true
|
||
},
|
||
{
|
||
label: "精彩站点",
|
||
prop: "at_wonderful_site_id",
|
||
type: "select",
|
||
get value() {
|
||
return drawerProps.value.row!.at_wonderful_site_id;
|
||
},
|
||
set value(val) {
|
||
drawerProps.value.row!.at_wonderful_site_id = val;
|
||
},
|
||
options: getSiteList.value,
|
||
fieldNames: { label: "si_name", value: "si_id" },
|
||
required: true
|
||
},
|
||
{
|
||
label: "推荐站点",
|
||
prop: "at_recommend_site_id",
|
||
type: "select",
|
||
get value() {
|
||
return drawerProps.value.row!.at_recommend_site_id;
|
||
},
|
||
set value(val) {
|
||
drawerProps.value.row!.at_recommend_site_id = val;
|
||
},
|
||
options: getSiteList.value,
|
||
fieldNames: { label: "si_name", value: "si_id" },
|
||
required: true
|
||
},
|
||
{
|
||
label: "图册封面",
|
||
prop: "at_cover_edit",
|
||
type: "text",
|
||
get value() {
|
||
return drawerProps.value.row!.at_cover_edit;
|
||
},
|
||
set value(val) {
|
||
drawerProps.value.row!.at_cover_edit = val;
|
||
},
|
||
required: true
|
||
},
|
||
|
||
{
|
||
label: "图册标签",
|
||
prop: "at_tag",
|
||
type: "tag",
|
||
get value() {
|
||
return drawerProps.value.row!.at_tag;
|
||
},
|
||
set value(val) {
|
||
drawerProps.value.row!.at_tag = val;
|
||
},
|
||
options: arrTagList.value,
|
||
fieldNames: { label: "label", value: "value" },
|
||
required: true
|
||
}
|
||
];
|
||
newColumns = [...columns];
|
||
if (drawerProps.value.title === "编辑图册") {
|
||
let columnsSon = [
|
||
{
|
||
label: "图册资源",
|
||
prop: "at_source",
|
||
type: "textarea-i",
|
||
describe: "多个用逗号(,)隔开",
|
||
get value() {
|
||
return drawerProps.value.row!.at_source;
|
||
},
|
||
set value(val) {
|
||
drawerProps.value.row!.at_source = val;
|
||
},
|
||
required: false
|
||
}
|
||
];
|
||
newColumns.splice(6, 0, ...columnsSon);
|
||
}
|
||
|
||
return newColumns;
|
||
});
|
||
const rules = ref();
|
||
|
||
interface DrawerProps {
|
||
title: string;
|
||
isView: boolean;
|
||
row: Partial<AtlasList.ResList>;
|
||
api?: (params: any) => Promise<any>;
|
||
getTableList?: () => void;
|
||
}
|
||
|
||
const drawerVisible = ref(false);
|
||
const drawerProps = ref<DrawerProps>({
|
||
isView: false,
|
||
title: "",
|
||
row: {}
|
||
});
|
||
|
||
// 接收父组件传过来的参数
|
||
const acceptParams = (params: DrawerProps) => {
|
||
drawerProps.value = params;
|
||
rules.value = generateRules(formColumns.value);
|
||
drawerVisible.value = true;
|
||
if (drawerProps.value.title === "新增图册") {
|
||
drawerProps.value.row!.at_status = 0;
|
||
}
|
||
TagList.value = [];
|
||
fileList.value = [];
|
||
if (drawerProps.value.row.at_cover) {
|
||
drawerProps.value.row.at_cover_code = drawerProps.value.row.at_cover.code;
|
||
drawerProps.value.row.at_cover_edit = drawerProps.value.row.at_cover.uri;
|
||
}
|
||
if (drawerProps.value.row.at_tag) {
|
||
TagList.value = drawerProps.value.row.at_tag.map((obj: Item) => obj.t_id);
|
||
}
|
||
if (drawerProps.value.row.at_source) {
|
||
const data = drawerProps.value.row.at_source_detail;
|
||
drawerProps.value.row.at_source = data;
|
||
const newArray = data.map(item => ({ url: item }));
|
||
fileList.value = newArray;
|
||
}
|
||
getTagList();
|
||
};
|
||
|
||
// 提交数据(新增/编辑)
|
||
const ruleFormRef = ref<FormInstance>();
|
||
const handleSubmit = () => {
|
||
console.log(drawerProps.value.row);
|
||
ruleFormRef.value!.validate(async valid => {
|
||
if (!valid) return;
|
||
try {
|
||
const params = { ...drawerProps.value.row };
|
||
let arrCoverData = {
|
||
uri: params.at_cover_edit,
|
||
code: params.at_cover_code
|
||
};
|
||
params.at_cover = arrCoverData;
|
||
params.at_tag = JSON.stringify(TagList.value);
|
||
if (drawerProps.value.title === "编辑图册") {
|
||
if (Array.isArray(params.at_source)) {
|
||
params.at_source = params.at_source.map(uri => ({ uri: extractURI(uri), code: params.at_cover_code }));
|
||
} else if (params.at_source != null) {
|
||
params.at_source = params.at_source.split(",");
|
||
params.at_source = params.at_source.map(uri => ({ uri: extractURI(uri), code: params.at_cover_code }));
|
||
}
|
||
}
|
||
await drawerProps.value.api!(params);
|
||
ElMessage.success({ message: `${drawerProps.value.title}成功!` });
|
||
drawerProps.value.getTableList!();
|
||
drawerVisible.value = false;
|
||
} catch (error) {
|
||
console.log(error);
|
||
}
|
||
});
|
||
};
|
||
|
||
defineExpose({
|
||
acceptParams
|
||
});
|
||
</script>
|
||
<style lang="scss">
|
||
.el-checkbox-button__inner {
|
||
margin-right: 10px;
|
||
margin-bottom: 10px;
|
||
border-left-color: #dcdfe6 !important;
|
||
}
|
||
.el-upload--picture-card {
|
||
display: none;
|
||
}
|
||
.img-more-h {
|
||
height: 300px;
|
||
overflow-y: scroll;
|
||
}
|
||
.tag-more-h {
|
||
margin-bottom: 10px;
|
||
}
|
||
.tag-more-s {
|
||
width: 100%;
|
||
padding: 10px;
|
||
border: 1px solid black;
|
||
box-shadow: 0 0 4px 2px rgb(0 0 0 / 90%);
|
||
}
|
||
</style>
|