This commit is contained in:
make
2025-04-06 21:51:01 +08:00
commit 3792acd4c7
408 changed files with 42349 additions and 0 deletions

View File

@@ -0,0 +1,390 @@
<template>
<el-drawer v-model="drawerVisible" :destroy-on-close="true" size="720" :title="`${drawerProps.title}`">
<el-form
ref="ruleFormRef"
label-width="100px"
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
type="textarea"
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-h">
<el-checkbox-group v-model="TagList" @change="placeableCheckedCitiesChange">
<el-checkbox-button class="vip-power" v-for="item in getTagList" :value="item.t_id" :key="item.t_id">
{{ item.t_name }}
</el-checkbox-button>
</el-checkbox-group>
</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 } 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 = ref([] as any);
getTypeList({ limit: 5000, page: 1, t_type: 2, t_hidden: 1 }).then(res => {
getTagList.value = res.data.item;
});
const getSiteList = ref([] as any);
getAllList().then(res => {
getSiteList.value = res.data.item;
});
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",
type: "text-i",
get value() {
return drawerProps.value.row!.at_cover;
},
set value(val) {
drawerProps.value.row!.at_cover = 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: getTagList.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;
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;
const data = drawerProps.value.row.at_cover_detail;
drawerProps.value.row.at_cover = data;
}
if (drawerProps.value.row.at_tag) {
TagList.value = drawerProps.value.row.at_tag.map((obj: Item) => {
obj.t_id = obj.t_id;
return 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;
}
};
//标签数组
async function placeableCheckedCitiesChange(value) {
drawerProps.value.row.at_tag = value;
TagList.value = value;
drawerProps.value.row.at_tag = JSON.stringify(drawerProps.value.row.at_tag);
}
// 提交数据(新增/编辑)
const ruleFormRef = ref<FormInstance>();
const handleSubmit = () => {
ruleFormRef.value!.validate(async valid => {
if (!valid) return;
try {
const params = { ...drawerProps.value.row };
let arrCoverData = {
uri: extractURI(params.at_cover),
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;
}
</style>

503
src/views/atlas/drawer.vue Normal file
View File

@@ -0,0 +1,503 @@
<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
type="textarea"
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>

View File

@@ -0,0 +1,233 @@
<template>
<el-dialog
v-model="drawerVisible"
:title="drawerProps.title + '--' + drawerProps.name"
:destroy-on-close="true"
width="1100px"
draggable
>
<el-form
ref="ruleFormRef"
label-width="0px"
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" />
</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" :label="item.value" size="large">{{
item.label
}}</el-radio>
</el-radio-group>
</template>
<template v-if="e.type === 'select'">
<el-select filterable 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 === 'tag'">
<el-tag
style="margin: 7px; margin-right: 10px"
v-for="item in drawerProps.row!.at_tag"
:key="item.t_id"
:type="item.type"
effect="dark"
>
{{ item.t_name }}
</el-tag>
</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 === 'textarea'">
<el-input
type="textarea"
:rows="10"
v-model="e.value"
maxlength="500000"
:placeholder="`请输入${e.label}`"
show-word-limit
/>
</template>
<template v-if="e.type === 'textareaMore'">
<el-input
type="textarea"
v-model="e.value"
maxlength="500000"
:placeholder="`请输入${e.label}`"
show-word-limit
/>
</template>
<template v-if="e.type === 'image'">
<div class="c-dialog-h">
<img v-for="(item, index) in e.value" :key="index" :src="item" alt="" />
</div>
</template>
<template v-if="e.type === 'textarea-i'">
<div :class="{ 'img-more-h': fileList.length > 0 }">
<UploadImgs disabled v-model:file-list="fileList" height="150px" width="19%" :drag="false">
<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 type="primary" @click="drawerVisible = false">确定</el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="UserDrawer">
import { ref, computed } from "vue";
import { FormInstance } from "element-plus";
import { AtlasList } from "@/api/interface/atlas/list";
import UploadImgs from "@/components/Upload/Imgs.vue";
type Options = {
label: string;
value: string | number;
};
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 };
};
const generateRules = (fields: Fields[]) => {
return fields.reduce(
(acc, field) => {
const { type, required, label, prop } = field;
let message = "";
if (type === "select") {
message = `请选择${label}`;
}
if (type === "text") {
message = `请输入${label}`;
}
if (type === "textarea") {
message = `请输入${label}`;
}
if (type === "textareaMore") {
message = `请输入${label}`;
}
if (type === "radio") {
message = `请选择${label}`;
}
acc[prop] = [{ required, message }];
return acc;
},
{} as Record<string, any>
);
};
const formColumns = computed((): Fields[] => {
return [
{
label: "",
prop: "at_source_detail",
// type: "image",
type: "textarea-i",
get value() {
return drawerProps.value.row!.at_source_detail;
},
set value(val) {
drawerProps.value.row!.at_source_detail = val;
},
options: drawerProps.value.row!.at_tag,
fieldNames: { label: "at_source_detail", value: "t_id" },
required: false
}
];
});
const rules = ref();
const fileList = ref([] as any);
interface DrawerProps {
title: string;
name?: any;
isView: boolean;
row: Partial<AtlasList.ResList>;
api?: (params: any) => Promise<any>;
getTableList?: () => void;
}
const drawerVisible = ref(false);
const drawerProps = ref<DrawerProps>({
isView: false,
title: "",
name: "",
row: {}
});
// 接收父组件传过来的参数
const acceptParams = async (params: DrawerProps) => {
drawerProps.value = params;
rules.value = generateRules(formColumns.value);
drawerProps.value.name = drawerProps.value.row!.at_name;
drawerVisible.value = true;
fileList.value = [];
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;
}
};
// 提交数据(新增/编辑)
const ruleFormRef = ref<FormInstance>();
defineExpose({
acceptParams
});
</script>
<style>
.el-dialog .el-dialog__header {
padding: 15px 0;
}
.c-dialog-h {
display: flex;
flex-wrap: wrap;
width: 1100px;
max-height: 600px;
overflow-y: scroll;
}
.c-dialog-h img {
width: 19.05%;
height: 150px;
margin-right: 10px;
margin-bottom: 10px;
}
.img-more-h .upload .upload-image {
object-fit: fill !important;
}
.img-more-h {
height: 520px;
margin-top: 15px;
overflow-y: scroll;
}
</style>

237
src/views/atlas/index.vue Normal file
View File

@@ -0,0 +1,237 @@
<template>
<div class="table-box">
<ProTable ref="proTable" highlight-current-row :columns="columns" :request-api="getList">
<!-- 表格 header 按钮 -->
<template #tableHeader="scope">
<el-button type="primary" style="display: none" :icon="CirclePlus" @click="openDrawer(true)">新增</el-button>
<el-button
type="danger"
:icon="Delete"
plain
:disabled="!scope.isSelected"
@click="batchDelete(scope.selectedList)"
>
批量删除图册
</el-button>
</template>
<!-- 表格操作 -->
<template #operation="scope">
<el-button type="primary" :icon="EditPen" @click="openDrawer(false, scope.row)">编辑</el-button>
<el-button type="danger" :icon="Delete" @click="deleteAccount(scope.row)">删除</el-button>
</template>
</ProTable>
<Drawer ref="drawerRef" />
<TagDialog ref="tagDialogRef" />
<ImgDialog ref="imgDialogRef" />
</div>
</template>
<script setup lang="tsx" name="complexProTable">
import { reactive, ref } from "vue";
import { AtlasList } from "@/api/interface/atlas/list";
import { useHandleData } from "@/hooks/useHandleData";
import ProTable from "@/components/ProTable/index.vue";
import { Delete, EditPen, CirclePlus } from "@element-plus/icons-vue";
import { ProTableInstance, ColumnProps } from "@/components/ProTable/interface";
import { getList, saveData, deleteItem } from "@/api/modules/atlas/list";
import Drawer from "./drawer.vue";
import TagDialog from "./tagDialog.vue";
import ImgDialog from "./imgDialog.vue";
import { getTypeList } from "@/api/modules/tag/list";
import { arrSxStatus } from "@/utils/serviceDict";
import { getAllList } from "@/api/modules/site/list";
// ProTable 实例
const proTable = ref<ProTableInstance>();
const getTagList = ref([] as any);
getTypeList({ limit: 5000, page: 1, t_type: 2, t_hidden: 1 }).then(res => {
getTagList.value = res.data.item;
});
// 表格配置项
const columns = reactive<ColumnProps<AtlasList.ResList>[]>([
{ type: "selection", fixed: "left", width: 50 },
{
prop: "key",
label: "搜索内容",
isShow: false,
search: {
el: "input"
}
},
{
prop: "at_id",
label: "ID",
width: 100
},
{
prop: "at_name",
label: "图册名称"
},
{
prop: "at_cover",
label: "图册封面",
width: 120,
render(scope) {
if (scope.row.at_cover) {
const data = scope.row.at_cover_detail;
//循环数组对某个对象key重新赋值
const modifiedArray = [] as any;
modifiedArray.push(data);
return (
<el-image
style="width: 98%; height: 30px;display: flex;align-items: center;"
src={modifiedArray[0]}
preview-src-list={modifiedArray}
hide-on-click-modal={true}
z-index={999999999}
preview-teleported={true}
fit="fill"
/>
);
}
}
},
{
prop: "at_source_detail",
label: "图册资源",
width: 120,
render(scope) {
if (scope.row.at_source_detail) {
return <el-tag onClick={() => openImgFun(scope.row)}>查看</el-tag>;
}
}
},
{
prop: "at_status",
label: "图册状态",
search: { el: "select", props: { filterable: true } },
enum: arrSxStatus,
render(scope) {
return (
<el-tag type={scope.row.at_status === 0 ? "danger" : "success"}>
{scope.row.at_status === 0 ? "下架" : "上架"}
</el-tag>
);
}
},
{
prop: "at_wonderful_site_id_detail",
label: "精彩站点",
render(scope) {
if (scope.row.at_wonderful_site_id_detail) {
return <span>{scope.row.at_wonderful_site_id_detail.join(", ")}</span>;
}
}
},
{
prop: "at_recommend_site_id_detail",
label: "推荐站点",
render(scope) {
if (scope.row.at_recommend_site_id_detail) {
return <span>{scope.row.at_recommend_site_id_detail.join(", ")}</span>;
}
}
},
{
prop: "at_wonderful_site_id",
label: "精彩站点",
isShow: false,
search: { el: "select", props: { filterable: true } },
fieldNames: { label: "si_name", value: "si_id" },
enum: getAllList
},
{
prop: "at_recommend_site_id",
label: "推荐站点",
isShow: false,
search: { el: "select", props: { filterable: true } },
fieldNames: { label: "si_name", value: "si_id" },
enum: getAllList
},
{
prop: "at_tag",
label: "标签",
width: 250,
// showOverflowTooltip: false,
// align: "left",
render(scope) {
if (scope.row.at_tag_detail) {
return <span>{scope.row.at_tag_detail.join(", ")}</span>;
}
}
},
{
prop: "at_tag",
label: "标签",
isShow: false,
search: { el: "select", props: { filterable: true } },
fieldNames: { label: "t_name", value: "t_id" },
enum: getTagList
},
{
prop: "at_count_play",
label: "查看次数"
},
{ prop: "created_at", label: "添加时间", width: 170 },
{ prop: "operation", label: "操作", fixed: "right", width: 230 }
]);
// 删除图册信息
const deleteAccount = async (params: AtlasList.ResList) => {
await useHandleData(deleteItem, { at_id: params.at_id }, "删除所选信息");
proTable.value?.getTableList();
};
// 批量删除图册信息
const batchDelete = async (id: any[]) => {
const ids = id.map((item: AtlasList.ResList) => item.at_id);
await useHandleData(deleteItem, { at_id: ids.join(",") }, "删除所选信息");
proTable.value?.clearSelection();
proTable.value?.getTableList();
};
// 打开 drawer(新增、编辑)
const drawerRef = ref<InstanceType<typeof Drawer> | null>(null);
const openDrawer = (isEdit: boolean, row: Partial<AtlasList.ResList> = {}) => {
const params = {
title: isEdit ? "新增图册" : "编辑图册",
row: { ...row },
api: saveData,
getTableList: proTable.value?.getTableList
};
drawerRef.value?.acceptParams(params as any);
};
// 标签
const tagDialogRef = ref<InstanceType<typeof TagDialog> | null>(null);
// const openTagFun = (row: Partial<AtlasList.ResList> = {}) => {
// const params = {
// title: "图册标签",
// row: { ...row },
// getTableList: proTable.value?.getTableList
// };
// tagDialogRef.value?.acceptParams(params as any);
// };
// 图册资源
const imgDialogRef = ref<InstanceType<typeof TagDialog> | null>(null);
const openImgFun = (row: Partial<AtlasList.ResList> = {}) => {
const params = {
title: "图册资源",
row: { ...row },
getTableList: proTable.value?.getTableList
};
imgDialogRef.value?.acceptParams(params as any);
};
</script>
<style lang="scss">
.el-table .warning-row,
.el-table .warning-row .el-table-fixed-column--right,
.el-table .warning-row .el-table-fixed-column--left {
background-color: var(--el-color-warning-light-9);
}
.el-table .success-row,
.el-table .success-row .el-table-fixed-column--right,
.el-table .success-row .el-table-fixed-column--left {
background-color: var(--el-color-success-light-9);
}
</style>

View File

@@ -0,0 +1,179 @@
<template>
<el-dialog v-model="drawerVisible" :title="drawerProps.title" :destroy-on-close="true" width="520px" draggable>
<el-form
ref="ruleFormRef"
label-width="50px"
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" />
</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" :label="item.value" size="large">{{
item.label
}}</el-radio>
</el-radio-group>
</template>
<template v-if="e.type === 'select'">
<el-select filterable 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 === 'tag'">
<el-tag
style="margin: 7px; margin-right: 10px"
v-for="item in drawerProps.row!.at_tag"
:key="item.t_id"
:type="item.type"
effect="dark"
>
{{ item.t_name }}
</el-tag>
</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 === 'textarea'">
<el-input
type="textarea"
:rows="10"
v-model="e.value"
maxlength="500000"
:placeholder="`请输入${e.label}`"
show-word-limit
/>
</template>
<template v-if="e.type === 'textareaMore'">
<el-input
type="textarea"
v-model="e.value"
maxlength="500000"
:placeholder="`请输入${e.label}`"
show-word-limit
/>
</template>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="drawerVisible = false">取消</el-button>
<el-button type="primary" @click="drawerVisible = false">确定</el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="UserDrawer">
import { ref, computed } from "vue";
import { FormInstance } from "element-plus";
import { AtlasList } from "@/api/interface/atlas/list";
type Options = {
label: string;
value: string | number;
};
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 };
};
const generateRules = (fields: Fields[]) => {
return fields.reduce(
(acc, field) => {
const { type, required, label, prop } = field;
let message = "";
if (type === "select") {
message = `请选择${label}`;
}
if (type === "text") {
message = `请输入${label}`;
}
if (type === "textarea") {
message = `请输入${label}`;
}
if (type === "textareaMore") {
message = `请输入${label}`;
}
if (type === "radio") {
message = `请选择${label}`;
}
acc[prop] = [{ required, message }];
return acc;
},
{} as Record<string, any>
);
};
const formColumns = computed((): Fields[] => {
return [
{
label: "标签",
prop: "t_name",
type: "tag",
get value() {
return drawerProps.value.row!.t_name;
},
set value(val) {
drawerProps.value.row!.t_name = val;
},
options: drawerProps.value.row!.at_tag,
fieldNames: { label: "t_name", value: "t_id" },
required: false
}
];
});
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 = async (params: DrawerProps) => {
drawerProps.value = params;
rules.value = generateRules(formColumns.value);
drawerVisible.value = true;
};
// 提交数据(新增/编辑)
const ruleFormRef = ref<FormInstance>();
defineExpose({
acceptParams
});
</script>
<style>
.el-dialog .el-dialog__header {
padding: 15px 0;
}
</style>