<template>
<BaseFormTemplate ref="BaseFormTemplate" :config="config"></BaseFormTemplate>
</template>
<script setup lang="ts">
import { useBaseFormTemplate } from "./hooks";
import {
getDeviceTypeThingIconStoreModel,
iconStoreDetails,
iconStoreSubmit,
iconStoreUpdateIcon,
} from "@/api/iot-thing-map/thing-icon-store.api";
import {
apiThingTypeList,
apiThingTypeTree,
} from "@/api/iot-thing-model/thing-type.api";
// import {
// apiGetThingModel,
// apiCreateThingModel,
// apiEditThingModel,
// apiGenerateIndent
// } from "@/api/iot-thing-model/thing-model-store.api";
/*****************Template+Hooks*****************/
const interAction = {
onClickSubmit,
getTitle,
getThingIconNamePopoverContent,
getThingIconDeviceTypePopoverContent,
getThingIconDeviceModelPopoverContent,
getIconUploadPopoverContent,
onChangeEvent,
mountedTemplate,
isEdit,
getThingTypeTree,
disabledThingType,
getThingTypeModelTree,
getDeviceModelList,
disabledThingTypeModel,
upload,
onUploadFileSuccess,
onFormUploadFileChange,
onUploadFileError,
onUploadFileRemove,
onUploadFilePreview,
};
const { config, template } = useBaseFormTemplate(
"BaseFormTemplate",
interAction
);
/********************Variable********************/
const route = useRoute();
const { proxy } = getCurrentInstance();
const detailObject = ref({});
const thingTypeTree = ref([]);
const hasData = ref(0);
const modelId = ref<{ value: string }>;
// 定义 deviceModelOptions 并初始化为空数组
const deviceModelOptions = ref<any>([]);
// 定义 deviceType:二维字符串数组
const deviceType = ref<string[][]>([]);
const deviceModel = ref<string[]>([]);
const { VITE_APP_API_BASE_FILE } = import.meta.env;
const filePathArr = ref([]);
const uploadFileFlag = ref(false);
/********************Function********************/
function getTitle() {
return isEdit()
? "menu.iot-thing-map.edit-thing-icon"
: "menu.iot-thing-map.add-thing-icon";
}
function mountedTemplate() {
initThingTypeTree();
if (isEdit()) {
executeApiGetDetail();
} else {
generateIndent();
}
}
function initThingTypeTree() {
apiThingTypeTree({})
.then((res) => {
thingTypeTree.value = res;
})
.catch((err) => {});
}
function disabledThingType() {
if (hasData.value == 1) {
return true;
} else {
return false;
}
}
function getThingTypeTree() {
return thingTypeTree.value;
}
function executeApiGetDetail() {
template.showLoading();
iconStoreDetails({ iconId: template.getRoute().query.id })
.then((res) => {
const tt = res.thingType;
if (!tt || !tt.productLineDictId || !tt.id) return null;
deviceType.value = [tt.productLineDictId, tt.id];
const ids: string[] = [];
getDeviceTypeThingIconStoreModel({ thingTypeId: tt.id }).then(
(response) => {
const deciveModelList: { label: string; value: number }[] = [];
response.forEach((e: { id: number; name: string }) => {
deciveModelList.push({
label: e.model,
value: e.id,
});
});
deviceModelOptions.value = deciveModelList;
}
);
res.thingTypeModelList.forEach((item) => {
if (item.id) {
ids.push(item.id);
}
});
deviceModel.value = ids;
detailObject.value = { ...res };
// if (res.fileUrl) {
// config.form.value.file = res.fileUrl;
// filePathArr.value = [res.fileUrl];
// }
template.getValues().name = res.iconName;
template.getValues().deviceType = deviceType.value;
template.getValues().deviceModel = deviceModel.value;
template.getValues().avatarUrl = res.fileUrl
template.getValues().description = res.description;
template.hideLoading();
})
.catch((err) => {
template.hideLoading();
});
}
function isEdit() {
return route.query.id != undefined;
// console.log('id',route.query.id)
// if(route.query.id != undefined) {
// console.log('1')
// return true
// } else {
// console.log('2')
// return false
// }
}
function getThingIconNamePopoverContent() {
return proxy.$t("message.create.thing.icon.store.name");
}
function getThingIconDeviceTypePopoverContent() {
return proxy.$t("message.create.thing.icon.store.deviceType");
}
function getThingIconDeviceModelPopoverContent() {
return proxy.$t("message.create.thing.icon.store.deviceModel");
}
function getIconUploadPopoverContent() {
return proxy.$t("message.create.thing.icon.store.upload");
}
function onChangeEvent(value: string, component: any) {
switch (component.value) {
case "deviceType":
// console.log("value", value);
if (!value || (Array.isArray(value) && value.length === 0)) {
// console.log("❌ 未选择任何值");
return;
}
let targetNode: TreeNode | undefined = undefined;
// 如果是数组,则按层级路径查找
if (Array.isArray(value)) {
// console.log("📎 收到路径数组:", value);
let currentNodes: TreeNode[] = thingTypeTree.value;
for (const levelValue of value) {
const matchedNode = currentNodes?.find(
(node) => node.value === levelValue
);
if (!matchedNode) {
console.error(`❌ 在当前层级找不到 value="${levelValue}" 的节点`);
return;
}
targetNode = matchedNode;
// 进入下一层级
currentNodes = (matchedNode.children as TreeNode[]) ?? [];
}
} else {
// 单值情况(非级联)
const findInTree = (nodes: TreeNode[]): TreeNode | undefined => {
for (const node of nodes) {
if (node.value === value) return node;
if (node.children && Array.isArray(node.children)) {
const found = findInTree(node.children);
if (found) return found;
}
}
return undefined;
};
targetNode = findInTree(thingTypeTree.value);
}
if (targetNode) {
// console.log("✅ 成功找到目标节点:", targetNode.value);
modelId.value = targetNode.value;
// console.log("targetNode.value", modelId.value);
// 可用于后续操作:设置图标、更新表单等
getThingTypeModelTree();
} else {
// console.error("❌ 未找到匹配节点");
}
break;
case "deviceModel":
// console.log("maodel value",value)
}
}
function getThingTypeModelTree() {
getDeviceTypeThingIconStoreModel({ thingTypeId: modelId.value })
.then((res) => {
// console.log('res',res)
const deciveModelList: { label: string; value: number }[] = [];
res.forEach((e: { id: number; name: string }) => {
deciveModelList.push({
label: e.model,
value: e.id,
});
});
deviceModelOptions.value = deciveModelList;
})
.catch((err) => {});
}
function getDeviceModelList() {
// console.log("deciveModelList", deviceModelOptions.value);
return deviceModelOptions.value;
}
function disabledThingTypeModel() {
if (deviceModelOptions.value.length == 0) {
return true;
} else {
return false;
}
}
function upload() {
return {
ref: "uploadFile",
url: VITE_APP_API_BASE_FILE + "/api/v1/aliyun-oss/uploadSimpleFile",
data: {
module: "iconStore",
},
disabled: false,
multiple: false,
// suffix: ["xls", "xlsx"],
suffix: ["png", "jpg", "jpeg", "gld", "gltf"],
name: "file",
buttonUpload: "button.selectFile",
popover: true,
popoverAttribute: {
popContent: proxy.$t("message.create.thing.icon.store.upload"),
},
showNotification: false,
useContentSlot: true,
size: 10,
unit: "MB",
autoUpload: false,
limit: 1,
onUploadSuccess: (response: any, file: any, fileList: any) => {
// ✅ 处理上传成功的响应
if (response?.code === 0 || response?.success) {
const fileUrl = response.data?.url;
if (fileUrl) {
config.form.value.file = fileUrl; // 存入表单
filePathArr.value = [fileUrl]; // 可选:维护一个数组
proxy.$message.success("上传成功");
}
} else {
proxy.$message.error(response.message || "上传失败");
onUploadFileError({ response, file, fileList });
}
},
onUploadError: (err: any) => {
uploadFileFlag.value = false;
proxy.$message.error("文件上传出错");
},
onRemoveChange: () => {
config.form.value.file = ""; // 清除表单值
filePathArr.value = [];
},
onFormUploadChange: (fileList: any) => {
interAction.onFormUploadFileChange(fileList);
},
onUploadPreview: () => {
interAction.onUploadFilePreview();
},
};
}
function onUploadFileSuccess(response: any, file: any, fileList: any) {
if (response?.code === 0 && response.data?.url) {
config.form.value.file = response.data.url;
filePathArr.value.push(response.data.url);
proxy.$message.success("上传成功");
} else {
proxy.$message.error(response.message || "上传失败");
}
}
function onFormUploadFileChange(files: any) {
// isUpdateFile.value = true;
console.log(files);
}
function onUploadFileError(files: any) {
uploadFileFlag.value = false;
}
function onUploadFileRemove() {}
function onUploadFilePreview() {}
function generateIndent() {
// template.showLoading();
// apiGenerateIndent({})
// .then((res) => {
// template.getValues().ident = res;
// template.hideLoading();
// })
// .catch((err) => {
// template.hideLoading();
// });
}
function submitForm() {}
function onClickSubmit() {
template.validate().then((result: any) => {
template.showLoading();
// console.log('Submit',isEdit());
if (isEdit()) {
// console.log("Edit", template.getValues());
const params = {
id: route.query.id,
iconName: template.getValues().name,
typeId: modelId.value,
modelIdList: template.getValues().deviceModel,
fileUrl: config.form.value.file, // ✅ 改为动态获取上传结果
// fileUrl: "jichufivewife1.png",
description: template.getValues().description,
};
iconStoreUpdateIcon(params)
.then((res) => {
template.hideLoading();
template.showNotification();
template.back();
})
.catch((err) => {
template.hideLoading();
});
// console.log("params", params);
// apiEditThingModel({
// id: route.query.id,
// thingModelName: template.getValues().name,
// identifier: template.getValues().ident,
// description: template.getValues().description,
// })
// .then((res) => {
// template.hideLoading();
// template.showNotification();
// template.back();
// })
// .catch((err) => {
// template.hideLoading();
// });
} else {
// console.log("Add", template.getValues());
const formValues = template.getValues();
if (!config.form.value.file) {
proxy.$message.warning("请先上传图标文件");
template.hideLoading();
return;
}
const params = {
iconName: template.getValues().name,
typeId: modelId.value,
modelIdList: template.getValues().deviceModel,
// fileUrl: config.form.value.file, // ✅ 改为动态获取上传结果
fileUrl: "jichufivewife1.png",
description: template.getValues().description,
};
// console.log("params", params);
iconStoreSubmit(params)
.then((res) => {
template.hideLoading();
template.showNotification();
template.back();
})
.catch((err) => {
template.hideLoading();
});
}
});
}
</script>
<style scoped lang="less">
.icon-store-form {
padding: 20px;
}
.image-preview-container {
margin-top: 20px;
display: flex;
align-items: center;
gap: 15px;
}
.form-label {
font-weight: 500;
color: #606266;
font-size: 14px;
white-space: nowrap;
}
.image-error-slot {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
background-color: #f5f5f5;
color: #999;
font-size: 12px;
}
</style>
vue文件
import { useBaseFormTemplateMethods } from "@/template/BaseFormTemplate/hooks";
export const useBaseFormTemplate = (refName: string, interAction?: any) => {
const config = reactive({
lifeCycle: {
mountedTemplate: () => {
interAction.mountedTemplate();
},
},
header: {
backBar: true,
titleBar: {
label: () => {
return interAction.getTitle();
},
},
buttonList: [
{
label: "button.submit",
onClick: () => {
interAction.onClickSubmit();
},
validator: () => {
return { show: !interAction.isEdit(), disabled: false };
},
},
{
label: "button.submit",
onClick: () => {
interAction.onClickSubmit();
},
validator: () => {
return { show: interAction.isEdit(), disabled: false };
},
},
],
},
form: {
onChangeEvent: (value: string, component: any) => {
interAction.onChangeEvent(value, component);
},
value: {
name: "",
// deviceType: "",
// deviceModel: [],
// file: [],
description: "",
},
components: [
{
name: "label.thing-icon-store-name",
type: "input",
value: "name",
required: true,
popover: (component: any, index: number) => {
return true;
},
popoverAttribute: (component: any) => {
return {
popContent: interAction.getThingIconNamePopoverContent(),
};
},
attribute: (component: any) => {
return {
formatter: (value: any) => {
return value;
},
parser: (value: any) => {
return value.replace(/[^a-zA-Z0-9\u4e00-\u9fa5_.]/g, "");
},
props: {
placeholder: "Please Input Icon Name",
},
};
},
},
{
name: "label.thing-icon-store-deviceType",
value: "deviceType",
type: "cascader",
required: true,
fullLine: true,
attribute: () => {
return {
filterable: true,
options: interAction.getThingTypeTree(),
props: {
multiple: false,
},
disabled: interAction.disabledThingType(),
// onChange: (value: any) => {
// console.log('modelValue changed:', value);
// }
};
},
},
{
name: "label.thing-icon-store-deviceModel",
type: "cascader",
value: "deviceModel",
required: true,
attribute: (component: any) => {
return {
options: interAction.getDeviceModelList(),
disabled: interAction.disabledThingTypeModel(),
props: {
multiple: true,
emitPath: false,
},
};
},
// type: "select",
// value: "deviceModel",
// required: true,
// attribute: (component: any) => ({
// return {
// options: interAction.getDeviceModelList(),
// disabled: interAction.disabledThingTypeModel(),
// };
// }),
},
{
type: "upload",
name: "label.thing-icon-store-upload",
value: "file",
required: true,
// popover: (component: any, index: number) => {
// return true;
// },
// popoverAttribute: (component: any) => {
// return {
// popContent: interAction.getIconUploadPopoverContent(),
// };
// },
uploadAttribute: (component: any) => {
return interAction.upload();
},
},
{
name: "label.thing-icon-store-current-icon",
type: "image",
value: "avatarUrl", // 对应 form.value.avatarUrl
imageWidth: "100px",
imageHeight: "100px",
preview: false, // 是否开启预览
},
// {
// name: "label.thing-icon-store-current-icon",
// type: "custom",
// value: "currentIconPreview", // 只是一个占位标识
// show: () => {
// console.log("[Debug] isEdit:", interAction.isEdit()); // 看输出是否符合预期
// return interAction.isEdit();
// },
// slot: "currentIconPreview", // 关键:指定一个插槽名
// },
{
name: "label.thing-icon-store-description",
type: "input",
value: "description",
required: true,
attribute: (component: any) => {
return {
type: "textarea",
maxlength: 500,
showWordLimit: true,
autosize: { minRows: 6, maxRows: 6 },
};
},
},
],
},
});
return { config, template: useBaseFormTemplateMethods(refName) };
};
ts文件
根据isEdit方法,进行控制Current Icon是否显示