first commit

This commit is contained in:
2026-03-05 16:52:12 +08:00
commit 8ca2e6d52f
1899 changed files with 321565 additions and 0 deletions

View File

@@ -0,0 +1,244 @@
<template>
<el-dialog v-model="visibleInner" :title="dialogTitle" width="820px" :close-on-click-modal="false">
<div class="sub mb-10px">分类{{ categoryName || '-' }}</div>
<el-card shadow="never">
<template #header>
<div class="card-header flex items-center justify-between">
<span>检查项目</span>
<div class="flex items-center gap-8px">
<el-button size="small" type="primary" plain @click="addItem">新增项目</el-button>
</div>
</div>
</template>
<div v-if="localItems.length === 0" class="text-gray-500">暂无项目点击"新增项目"添加</div>
<div v-for="(item, idx) in localItems" :key="item.code" class="item-row">
<div class="item-left">
<div class="item-name flex items-center">
<span class="mr-8px">{{ idx + 1 }}.</span>
<el-input v-model="item.name" placeholder="检查项名称" size="small" style="width: 240px; margin-right: 12px;" />
<el-input v-model="item.desc" placeholder="检查项说明(可选)" size="small" style="width: 480px;" />
</div>
</div>
<div class="item-right">
<el-radio-group v-model="item.result" @change="onResultChange(item)">
<el-radio label="ok">正常</el-radio>
<el-radio label="bad">异常</el-radio>
</el-radio-group>
<div class="inline-actions ml-12px">
<el-button size="small" text @click="moveUp(idx)" :disabled="idx === 0">上移</el-button>
<el-button size="small" text @click="moveDown(idx)" :disabled="idx === localItems.length - 1">下移</el-button>
<el-button size="small" text type="danger" @click="removeItem(idx)">删除</el-button>
</div>
</div>
<div v-if="item.result === 'bad'" class="issue-area">
<el-input v-model="item.issueDesc" type="textarea" :rows="2" placeholder="请描述异常" />
<el-upload
class="mt-8px"
action=""
:http-request="handleUpload"
list-type="picture-card"
:file-list="item.photos"
:on-remove="(file, list) => onRemove(file, list, item)"
:on-preview="(file) => onPreview(file)"
:limit="10"
multiple
accept="image/*">
<el-icon>
<Plus />
</el-icon>
</el-upload>
</div>
</div>
</el-card>
<template #footer>
<div class="dialog-footer">
<el-button @click="close">取消</el-button>
<el-button type="primary" @click="save">保存</el-button>
</div>
</template>
</el-dialog>
<!-- 图片预览对话框 -->
<el-dialog v-model="previewVisible" title="图片预览" width="80%" center>
<img :src="previewImageUrl" style="width: 100%; max-height: 70vh; object-fit: contain;" />
</el-dialog>
</template>
<script lang="ts" setup>
import { Plus } from '@element-plus/icons-vue'
import { CheckLogApi } from '@/api/iot/check/log'
const props = defineProps<{
modelValue?: boolean
categoryName?: string
items?: Array<{ name: string; desc?: string }>
title?: string
}>()
const emit = defineEmits(['update:modelValue', 'save'])
const visibleInner = computed({
get: () => !!props.modelValue,
set: (v: boolean) => emit('update:modelValue', v)
})
const dialogTitle = computed(() => props.title || '新增模板')
interface ExecItem {
code: string
name: string
desc?: string
result?: 'ok' | 'bad'
issueDesc?: string
photos: any[]
}
const localItems = ref<ExecItem[]>([])
// 图片预览相关
const previewVisible = ref(false)
const previewImageUrl = ref('')
// 初始化本地数据
const initLocalItems = () => {
const defs = (props.items || []).length ? props.items! : []
localItems.value = defs.map((def, idx) => ({
code: `CHK-${String(idx + 1).padStart(3, '0')}`,
name: def.name,
desc: def.desc,
result: 'ok',
issueDesc: '',
photos: []
}))
}
onMounted(() => {
initLocalItems()
})
// 监听props.items变化用于编辑时回显数据
watch(() => props.items, () => {
initLocalItems()
}, { deep: true })
function addItem() {
const nextIndex = localItems.value.length + 1
localItems.value.push({
code: `CHK-${String(nextIndex).padStart(3, '0')}`,
name: '',
desc: '',
result: 'ok',
issueDesc: '',
photos: []
})
}
function removeItem(idx: number) {
localItems.value.splice(idx, 1)
// 重排 code
localItems.value.forEach((it, i) => (it.code = `CHK-${String(i + 1).padStart(3, '0')}`))
}
function moveUp(i: number) {
if (i <= 0) return
const t = localItems.value[i - 1]
localItems.value[i - 1] = localItems.value[i]
localItems.value[i] = t
}
function moveDown(i: number) {
if (i >= localItems.value.length - 1) return
const t = localItems.value[i + 1]
localItems.value[i + 1] = localItems.value[i]
localItems.value[i] = t
}
function onResultChange(item: ExecItem) {
if (item.result !== 'bad') {
item.issueDesc = ''
item.photos = []
}
}
async function handleUpload(options: any) {
const { file, onSuccess, onError } = options
try {
const res = await CheckLogApi.uploadImage(file as File)
onSuccess({ url: res.url, name: res.name })
} catch (e) {
onError(e)
}
}
function onRemove(_: any, list: any[], item: ExecItem) {
item.photos = list
}
// 图片预览
function onPreview(file: any) {
previewImageUrl.value = file.url || file.response?.url
previewVisible.value = true
}
function save() {
const payload = localItems.value.map(it => ({ name: (it.name || '').trim(), desc: (it.desc || '').trim() }))
if (!payload.length) {
ElMessage.warning('请至少新增一个检查项')
return
}
emit('save', payload)
visibleInner.value = false
}
function close() {
visibleInner.value = false
}
</script>
<style scoped>
.sub {
color: #666;
font-size: 12px;
}
.card-header {
font-weight: 600;
}
.item-row {
border-bottom: 1px solid #f0f0f0;
padding: 12px 0;
}
.item-left {
margin-bottom: 8px;
}
.item-name {
font-weight: 600;
}
.item-desc {
color: #666;
font-size: 12px;
margin-top: 4px;
}
.item-right {
margin: 8px 0;
}
.issue-area {
margin-top: 8px;
}
.text-gray-500 {
color: #909399;
}
.hint {
color: #909399;
font-size: 12px;
}
.mt-8px {
margin-top: 8px;
}
</style>