Files
SEONexusAdmin/src/views/atlas/drawer.vue
2025-04-09 09:51:46 +08:00

504 lines
18 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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>