This commit is contained in:
zlwu committed 2026-08-21 16:50:47 +08:00
1 parent 6dc8cfb2c1
commit d26fb23fcf
181 files changed
+25727 -24557

No files matched your search

@@ -0,0 +1,152 @@
<template>
<el-select
:model-value="modelValue"
:placeholder="placeholder"
:clearable="clearable"
:filterable="filterable"
:allow-create="allowCreate"
:default-first-option="allowCreate"
:disabled="disabled || loading"
:size="size"
:style="style"
@update:model-value="onSelect"
>
<el-option
v-for="opt in options"
:key="opt.value"
:label="opt.label"
:value="opt.value"
>
<div class="dict-select-option">
<span>{{ opt.label }}</span>
<el-button
v-if="dictStore.isSessionCreated(dictType, opt.value)"
link
type="danger"
size="small"
class="dict-select-option__remove"
@click.stop="onRemove(opt.value)"
>
<el-icon><Delete /></el-icon>
</el-button>
</div>
</el-option>
</el-select>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import { Delete } from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { useDictStore } from '@/store/modules/dict'
const props = withDefaults(
defineProps<{
dictType: string
modelValue?: string | null
allowCreate?: boolean
clearable?: boolean
filterable?: boolean
disabled?: boolean
placeholder?: string
size?: '' | 'default' | 'small' | 'large'
style?: string | Record<string, string>
}>(),
{
modelValue: '',
allowCreate: true,
clearable: true,
filterable: true,
disabled: false,
placeholder: '请选择',
size: 'default'
}
)
const emit = defineEmits<{
'update:modelValue': [value: string]
change: [value: string]
}>()
const dictStore = useDictStore()
const loading = ref(false)
const options = computed(() => dictStore.getOptions(props.dictType))
function isKnown(value: string) {
return options.value.some((o) => o.value === value || o.label === value)
}
async function onSelect(raw: string | null | undefined) {
const next = raw == null ? '' : String(raw).trim()
if (!next) {
emit('update:modelValue', '')
emit('change', '')
return
}
if (!props.allowCreate || isKnown(next)) {
const known = options.value.find((o) => o.value === next || o.label === next)
const value = known?.value ?? next
emit('update:modelValue', value)
emit('change', value)
return
}
try {
await ElMessageBox.confirm(`将「${next}」新增到字典,是否继续?`, '新增字典项', {
type: 'warning',
confirmButtonText: '确定',
cancelButtonText: '取消'
})
} catch {
return
}
loading.value = true
try {
const data = await dictStore.ensure(props.dictType, next)
emit('update:modelValue', data.value)
emit('change', data.value)
} catch (e: any) {
ElMessage.error(e?.message || '写入字典失败')
} finally {
loading.value = false
}
}
async function onRemove(value: string) {
try {
await ElMessageBox.confirm(`确定从字典中删除「${value}」吗?`, '删除字典项', {
type: 'warning',
confirmButtonText: '删除',
cancelButtonText: '取消'
})
} catch {
return
}
loading.value = true
try {
await dictStore.ensureRemove(props.dictType, value)
if (props.modelValue === value) {
emit('update:modelValue', '')
emit('change', '')
}
ElMessage.success('已删除')
} catch (e: any) {
ElMessage.error(e?.message || '删除失败')
} finally {
loading.value = false
}
}
</script>
<style scoped>
.dict-select-option {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.dict-select-option__remove {
flex-shrink: 0;
}
</style>