初始化

This commit is contained in:
2026-02-22 18:56:10 +08:00
commit 26677972a6
3112 changed files with 255972 additions and 0 deletions

View File

@@ -0,0 +1,77 @@
<script setup lang="ts">
import { ref, watch } from 'vue';
import { ElButton, ElDialog } from 'element-plus';
import PageData from '#/components/page/PageData.vue';
import { $t } from '#/locales';
import ResourceCardList from '#/views/ai/resource/ResourceCardList.vue';
const props = defineProps({
attrName: {
type: String,
required: true,
},
});
const emit = defineEmits(['choose']);
const pageDataRef = ref();
const dialogVisible = ref(false);
const chooseResources = ref([]);
const currentChoose = ref<any>({});
function openDialog() {
dialogVisible.value = true;
}
function closeDialog() {
dialogVisible.value = false;
}
function confirm() {
emit('choose', currentChoose.value, props.attrName);
closeDialog();
}
watch(
() => chooseResources.value,
(newValue) => {
currentChoose.value = newValue.length > 0 ? newValue[0] : {};
},
);
</script>
<template>
<div>
<ElDialog
v-model="dialogVisible"
draggable
:title="$t('aiResource.choose')"
:before-close="closeDialog"
:close-on-click-modal="false"
width="80%"
destroy-on-close
>
<PageData
ref="pageDataRef"
page-url="/api/v1/resource/page"
:page-size="8"
:page-sizes="[8, 12, 16, 20]"
>
<template #default="{ pageList }">
<ResourceCardList v-model="chooseResources" :data="pageList" />
</template>
</PageData>
<template #footer>
<ElButton @click="closeDialog">
{{ $t('button.cancel') }}
</ElButton>
<ElButton type="primary" @click="confirm">
{{ $t('button.confirm') }}
</ElButton>
</template>
</ElDialog>
<ElButton @click="openDialog()">
{{ $t('button.choose') }}
</ElButton>
</div>
</template>
<style scoped></style>

View File

@@ -0,0 +1,47 @@
<script setup lang="ts">
import { ref } from 'vue';
import { ElDialog, ElImage } from 'element-plus';
defineExpose({
openDialog,
});
const dialogVisible = ref(false);
const data = ref<any>();
function openDialog(row: any) {
data.value = row;
dialogVisible.value = true;
}
function closeDialog() {
dialogVisible.value = false;
}
</script>
<template>
<ElDialog
v-model="dialogVisible"
draggable
:title="$t('message.preview')"
:before-close="closeDialog"
:close-on-click-modal="false"
destroy-on-close
>
<div class="flex justify-center">
<ElImage
v-if="data.resourceType === 0"
style="width: 200px"
:preview-src-list="[data.resourceUrl]"
:src="data.resourceUrl"
/>
<video v-if="data.resourceType === 1" controls width="640" height="360">
<source :src="data.resourceUrl" type="video/mp4" />
{{ $t('message.notVideo') }}
</video>
<audio v-if="data.resourceType === 2" controls :src="data.resourceUrl">
{{ $t('message.notAudio') }}
</audio>
</div>
</ElDialog>
</template>
<style scoped></style>

View File

@@ -0,0 +1,155 @@
<script setup lang="ts">
import { onMounted, ref, watch } from 'vue';
import {
ElCard,
ElCheckbox,
ElCol,
ElImage,
ElRadio,
ElRow,
ElText,
ElTooltip,
} from 'element-plus';
import Tag from '#/components/tag/Tag.vue';
import { $t } from '#/locales';
import { useDictStore } from '#/store';
import {
getResourceOriginColor,
getResourceTypeColor,
getSrc,
} from '#/utils/resource';
import PreviewModal from '#/views/ai/resource/PreviewModal.vue';
export interface ResourceCardProps {
data: any[];
multiple?: boolean;
valueProp?: string;
}
const props = withDefaults(defineProps<ResourceCardProps>(), {
multiple: false,
valueProp: 'id',
});
const emit = defineEmits(['update:modelValue']);
onMounted(() => {
initDict();
});
const dictStore = useDictStore();
function initDict() {
dictStore.fetchDictionary('resourceType');
dictStore.fetchDictionary('resourceOriginType');
}
const previewDialog = ref();
const radioValue = ref('');
const checkAll = ref(false);
function choose() {
const arr = [];
if (props.multiple) {
for (const data of props.data) {
if (data.checkboxValue) {
arr.push(data);
}
}
} else {
if (radioValue.value) {
for (const data of props.data) {
if (data[props.valueProp] === radioValue.value) {
arr.push(data);
}
}
}
}
emit('update:modelValue', arr);
}
function handleCheckAllChange(val: any) {
if (val) {
for (const data of props.data) {
data.checkboxValue = data[props.valueProp];
}
} else {
for (const data of props.data) {
data.checkboxValue = '';
}
}
}
function preview(row: any) {
previewDialog.value.openDialog({ ...row });
}
watch(
[() => radioValue.value, () => props.data],
() => {
choose();
},
{ deep: true },
);
</script>
<template>
<div>
<PreviewModal ref="previewDialog" />
<ElCheckbox
v-if="multiple"
:label="$t('button.selectAll')"
v-model="checkAll"
@change="handleCheckAllChange"
/>
<ElRow :gutter="20">
<ElCol :span="6" v-for="item in data" :key="item.id" class="mb-5">
<ElCard
:body-style="{ padding: '12px', height: '285px' }"
shadow="hover"
>
<div>
<div>
<ElCheckbox
v-if="multiple"
v-model="item.checkboxValue"
:true-value="item[valueProp]"
false-value=""
/>
<ElRadio v-else v-model="radioValue" :value="item[valueProp]" />
</div>
<div>
<ElImage
@click="preview(item)"
:src="getSrc(item)"
style="width: 100%; height: 150px; cursor: pointer"
/>
</div>
<div>
<ElTooltip
:content="`${item.resourceName}.${item.suffix}`"
placement="top"
>
<ElText truncated>
{{ item.resourceName }}.{{ item.suffix }}
</ElText>
</ElTooltip>
</div>
<div class="flex gap-1.5">
<Tag
size="small"
:background-color="`${getResourceOriginColor(item)}15`"
:text-color="getResourceOriginColor(item)"
:text="
dictStore.getDictLabel('resourceOriginType', item.origin)
"
/>
<Tag
size="small"
:background-color="`${getResourceTypeColor(item)}15`"
:text-color="getResourceTypeColor(item)"
:text="
dictStore.getDictLabel('resourceType', item.resourceType)
"
/>
</div>
</div>
</ElCard>
</ElCol>
</ElRow>
</div>
</template>
<style scoped></style>

View File

@@ -0,0 +1,467 @@
<script setup lang="ts">
import type { FormInstance } from 'element-plus';
import { computed, onMounted, ref } from 'vue';
import { formatBytes } from '@easyflow/utils';
import {
Delete,
Download,
Edit,
MoreFilled,
Plus,
} from '@element-plus/icons-vue';
import {
ElAvatar,
ElButton,
ElDialog,
ElDropdown,
ElDropdownItem,
ElDropdownMenu,
ElForm,
ElFormItem,
ElIcon,
ElInput,
ElInputNumber,
ElMessage,
ElMessageBox,
ElTable,
ElTableColumn,
ElText,
} from 'element-plus';
import { tryit } from 'radash';
import { api } from '#/api/request';
import DictSelect from '#/components/dict/DictSelect.vue';
import PageData from '#/components/page/PageData.vue';
import PageSide from '#/components/page/PageSide.vue';
import Tag from '#/components/tag/Tag.vue';
import { $t } from '#/locales';
import { useDictStore } from '#/store';
import {
getResourceOriginColor,
getResourceTypeColor,
getSrc,
} from '#/utils/resource';
import PreviewModal from '#/views/ai/resource/PreviewModal.vue';
import ResourceModal from './ResourceModal.vue';
onMounted(() => {
initDict();
getSideList();
});
const formRef = ref<FormInstance>();
const pageDataRef = ref();
const saveDialog = ref();
const previewDialog = ref();
const formInline = ref({
resourceName: '',
resourceType: '',
});
const dictStore = useDictStore();
function initDict() {
dictStore.fetchDictionary('resourceType');
dictStore.fetchDictionary('resourceOriginType');
}
function search(formEl: FormInstance | undefined) {
formEl?.validate((valid) => {
if (valid) {
pageDataRef.value.setQuery(formInline.value);
}
});
}
function reset(formEl: FormInstance | undefined) {
formEl?.resetFields();
pageDataRef.value.setQuery({});
}
function showDialog(row: any) {
saveDialog.value.openDialog({ ...row });
}
function remove(row: any) {
ElMessageBox.confirm($t('message.deleteAlert'), $t('message.noticeTitle'), {
confirmButtonText: $t('message.ok'),
cancelButtonText: $t('message.cancel'),
type: 'warning',
beforeClose: (action, instance, done) => {
if (action === 'confirm') {
instance.confirmButtonLoading = true;
api
.post('/api/v1/resource/remove', { id: row.id })
.then((res) => {
instance.confirmButtonLoading = false;
if (res.errorCode === 0) {
ElMessage.success(res.message);
reset(formRef.value);
done();
}
})
.catch(() => {
instance.confirmButtonLoading = false;
});
} else {
done();
}
},
}).catch(() => {});
}
function preview(row: any) {
previewDialog.value.openDialog({ ...row });
}
function download(row: any) {
window.open(row.resourceUrl, '_blank');
}
const fieldDefinitions = ref<any[]>([
{
prop: 'categoryName',
label: $t('aiWorkflowCategory.categoryName'),
type: 'input',
required: true,
placeholder: $t('aiWorkflowCategory.categoryName'),
},
{
prop: 'sortNo',
label: $t('aiWorkflowCategory.sortNo'),
type: 'number',
required: false,
placeholder: $t('aiWorkflowCategory.sortNo'),
},
]);
const sideList = ref<any>([]);
const controlBtns = [
{
icon: Edit,
label: $t('button.edit'),
onClick(row: any) {
showControlDialog(row);
},
},
{
type: 'danger',
icon: Delete,
label: $t('button.delete'),
onClick(row: any) {
removeCategory(row);
},
},
];
const footerButton = {
icon: Plus,
label: $t('button.add'),
onClick() {
showControlDialog({});
},
};
const sideDialogVisible = ref(false);
const sideFormData = ref<any>({});
const sideFormRef = ref<FormInstance>();
const sideFormRules = computed(() => {
const rules: Record<string, any[]> = {};
fieldDefinitions.value.forEach((field) => {
const fieldRules = [];
if (field.required) {
fieldRules.push({
required: true,
message: `${$t('message.required')}`,
trigger: 'blur',
});
}
if (fieldRules.length > 0) {
rules[field.prop] = fieldRules;
}
});
return rules;
});
const sideSaveLoading = ref(false);
function changeCategory(category: any) {
pageDataRef.value.setQuery({ categoryId: category.id });
}
function showControlDialog(item: any) {
sideFormRef.value?.resetFields();
sideFormData.value = { ...item };
sideDialogVisible.value = true;
}
function removeCategory(row: any) {
ElMessageBox.confirm($t('message.deleteAlert'), $t('message.noticeTitle'), {
confirmButtonText: $t('message.ok'),
cancelButtonText: $t('message.cancel'),
type: 'warning',
beforeClose: (action, instance, done) => {
if (action === 'confirm') {
instance.confirmButtonLoading = true;
api
.post('/api/v1/resourceCategory/remove', { id: row.id })
.then((res) => {
instance.confirmButtonLoading = false;
if (res.errorCode === 0) {
ElMessage.success(res.message);
done();
getSideList();
}
})
.catch(() => {
instance.confirmButtonLoading = false;
});
} else {
done();
}
},
}).catch(() => {});
}
function handleSideSubmit() {
formRef.value?.validate((valid) => {
if (valid) {
sideSaveLoading.value = true;
const url = sideFormData.value.id
? '/api/v1/resourceCategory/update'
: '/api/v1/resourceCategory/save';
api.post(url, sideFormData.value).then((res) => {
sideSaveLoading.value = false;
if (res.errorCode === 0) {
ElMessage.success(res.message);
sideDialogVisible.value = false;
getSideList();
}
});
}
});
}
const getSideList = async () => {
const [, res] = await tryit(api.get)('/api/v1/resourceCategory/list', {
params: { sortKey: 'sortNo', sortType: 'asc' },
});
if (res && res.errorCode === 0) {
sideList.value = [
{
id: '',
categoryName: $t('common.allCategories'),
},
...res.data,
];
}
};
</script>
<template>
<div class="flex h-full flex-col gap-1.5 p-6">
<PreviewModal ref="previewDialog" />
<ResourceModal ref="saveDialog" @reload="reset" />
<div class="flex items-center justify-between">
<ElForm ref="formRef" inline :model="formInline">
<ElFormItem prop="resourceType" class="!mr-3">
<DictSelect
v-model="formInline.resourceType"
dict-code="resourceType"
:placeholder="$t('aiResource.resourceType')"
/>
</ElFormItem>
<ElFormItem prop="resourceName" class="!mr-3">
<ElInput
v-model="formInline.resourceName"
:placeholder="$t('aiResource.resourceName')"
/>
</ElFormItem>
<ElFormItem>
<ElButton @click="search(formRef)" type="primary">
{{ $t('button.query') }}
</ElButton>
<ElButton @click="reset(formRef)">
{{ $t('button.reset') }}
</ElButton>
</ElFormItem>
</ElForm>
<div class="handle-div">
<ElButton
v-access:code="'/api/v1/resource/save'"
@click="showDialog({})"
type="primary"
>
<ElIcon class="mr-1">
<Plus />
</ElIcon>
{{ $t('button.add') }}
</ElButton>
</div>
</div>
<div class="flex max-h-[calc(100vh-191px)] flex-1 gap-6">
<PageSide
label-key="categoryName"
value-key="id"
:menus="sideList"
:control-btns="controlBtns"
:footer-button="footerButton"
@change="changeCategory"
/>
<div class="bg-background h-full flex-1 overflow-auto rounded-lg p-5">
<PageData
ref="pageDataRef"
page-url="/api/v1/resource/page"
:page-size="10"
>
<template #default="{ pageList }">
<ElTable :data="pageList" border>
<ElTableColumn
prop="resourceName"
:label="$t('aiResource.resourceName')"
width="300"
>
<template #default="{ row }">
<div class="flex items-center gap-2.5">
<ElAvatar :src="getSrc(row)" shape="square" :size="32" />
<ElText truncated>
{{ row.resourceName }}
</ElText>
</div>
</template>
</ElTableColumn>
<ElTableColumn
align="center"
prop="suffix"
:label="$t('aiResource.suffix')"
width="60"
>
<template #default="{ row }">
{{ row.suffix }}
</template>
</ElTableColumn>
<ElTableColumn
align="center"
prop="fileSize"
:label="$t('aiResource.fileSize')"
>
<template #default="{ row }">
{{ formatBytes(row.fileSize) }}
</template>
</ElTableColumn>
<ElTableColumn
align="center"
prop="origin"
:label="$t('aiResource.origin')"
>
<template #default="{ row }">
<Tag
size="small"
:background-color="`${getResourceOriginColor(row)}15`"
:text-color="getResourceOriginColor(row)"
:text="
dictStore.getDictLabel('resourceOriginType', row.origin)
"
/>
</template>
</ElTableColumn>
<ElTableColumn
align="center"
prop="resourceType"
:label="$t('aiResource.resourceType')"
>
<template #default="{ row }">
<Tag
size="small"
:background-color="`${getResourceTypeColor(row)}15`"
:text-color="getResourceTypeColor(row)"
:text="
dictStore.getDictLabel('resourceType', row.resourceType)
"
/>
</template>
</ElTableColumn>
<ElTableColumn prop="created" :label="$t('aiResource.created')">
<template #default="{ row }">
{{ row.created }}
</template>
</ElTableColumn>
<ElTableColumn
:label="$t('common.handle')"
width="140"
align="right"
>
<template #default="{ row }">
<div class="flex items-center gap-3">
<div class="flex items-center">
<ElButton link type="primary" @click="preview(row)">
{{ $t('button.view') }}
</ElButton>
<ElButton link type="primary" @click="showDialog(row)">
{{ $t('button.edit') }}
</ElButton>
</div>
<ElDropdown>
<ElButton link :icon="MoreFilled" />
<template #dropdown>
<ElDropdownMenu>
<ElDropdownItem @click="download(row)">
<ElButton :icon="Download" link>
{{ $t('button.download') }}
</ElButton>
</ElDropdownItem>
<div v-access:code="'/api/v1/resource/remove'">
<ElDropdownItem @click="remove(row)">
<ElButton type="danger" :icon="Delete" link>
{{ $t('button.delete') }}
</ElButton>
</ElDropdownItem>
</div>
</ElDropdownMenu>
</template>
</ElDropdown>
</div>
</template>
</ElTableColumn>
</ElTable>
</template>
</PageData>
</div>
</div>
<ElDialog
v-model="sideDialogVisible"
:title="sideFormData.id ? `${$t('button.edit')}` : `${$t('button.add')}`"
:close-on-click-modal="false"
>
<ElForm
ref="sideFormRef"
:model="sideFormData"
:rules="sideFormRules"
label-width="120px"
>
<!-- 动态生成表单项 -->
<ElFormItem
v-for="field in fieldDefinitions"
:key="field.prop"
:label="field.label"
:prop="field.prop"
>
<ElInput
v-if="!field.type || field.type === 'input'"
v-model="sideFormData[field.prop]"
:placeholder="field.placeholder"
/>
<ElInputNumber
v-else-if="field.type === 'number'"
v-model="sideFormData[field.prop]"
:placeholder="field.placeholder"
style="width: 100%"
/>
</ElFormItem>
</ElForm>
<template #footer>
<ElButton @click="sideDialogVisible = false">
{{ $t('button.cancel') }}
</ElButton>
<ElButton
type="primary"
@click="handleSideSubmit"
:loading="sideSaveLoading"
>
{{ $t('button.confirm') }}
</ElButton>
</template>
</ElDialog>
</div>
</template>

View File

@@ -0,0 +1,168 @@
<script setup lang="ts">
import type { FormInstance } from 'element-plus';
import { onMounted, ref } from 'vue';
import { getResourceType } from '@easyflow/utils';
import {
ElButton,
ElDialog,
ElForm,
ElFormItem,
ElInput,
ElMessage,
} from 'element-plus';
import { api } from '#/api/request';
import DictSelect from '#/components/dict/DictSelect.vue';
import Upload from '#/components/upload/Upload.vue';
import { $t } from '#/locales';
const emit = defineEmits(['reload']);
// vue
onMounted(() => {});
defineExpose({
openDialog,
});
const saveForm = ref<FormInstance>();
// variables
const dialogVisible = ref(false);
const isAdd = ref(true);
const entity = ref<any>({
deptId: '',
resourceType: '',
resourceName: '',
suffix: '',
resourceUrl: '',
origin: '',
status: '',
options: '',
fileSize: '',
});
const btnLoading = ref(false);
const rules = ref({
deptId: [
{ required: true, message: $t('message.required'), trigger: 'blur' },
],
resourceType: [
{ required: true, message: $t('message.required'), trigger: 'blur' },
],
resourceName: [
{ required: true, message: $t('message.required'), trigger: 'blur' },
],
suffix: [
{ required: true, message: $t('message.required'), trigger: 'blur' },
],
resourceUrl: [
{ required: true, message: $t('message.required'), trigger: 'blur' },
],
origin: [
{ required: true, message: $t('message.required'), trigger: 'blur' },
],
status: [
{ required: true, message: $t('message.required'), trigger: 'blur' },
],
});
// functions
function openDialog(row: any) {
if (row.id) {
isAdd.value = false;
}
entity.value = row;
dialogVisible.value = true;
}
function save() {
saveForm.value?.validate((valid) => {
if (valid) {
btnLoading.value = true;
api
.post(
isAdd.value ? 'api/v1/resource/save' : 'api/v1/resource/update',
entity.value,
)
.then((res) => {
btnLoading.value = false;
if (res.errorCode === 0) {
ElMessage.success(res.message);
emit('reload');
closeDialog();
}
})
.catch(() => {
btnLoading.value = false;
});
}
});
}
function closeDialog() {
saveForm.value?.resetFields();
isAdd.value = true;
entity.value = {};
dialogVisible.value = false;
}
function beforeUpload(f: any) {
const fName = f?.name?.split('.')[0];
const fExt = f?.name?.split('.')[1];
entity.value.resourceType = getResourceType(fExt);
entity.value.resourceName = fName;
entity.value.suffix = fExt;
entity.value.fileSize = f.size;
entity.value.origin = 0;
}
function uploadSuccess(res: any) {
entity.value.resourceUrl = res;
}
</script>
<template>
<ElDialog
v-model="dialogVisible"
draggable
:title="isAdd ? $t('button.add') : $t('button.edit')"
:before-close="closeDialog"
:close-on-click-modal="false"
>
<ElForm
label-width="120px"
ref="saveForm"
:model="entity"
status-icon
:rules="rules"
>
<ElFormItem prop="resourceUrl" :label="$t('aiResource.resourceUrl')">
<Upload @before-upload="beforeUpload" @success="uploadSuccess" />
</ElFormItem>
<ElFormItem prop="origin" :label="$t('aiResource.origin')">
<DictSelect v-model="entity.origin" dict-code="resourceOriginType" />
</ElFormItem>
<ElFormItem prop="resourceType" :label="$t('aiResource.resourceType')">
<DictSelect v-model="entity.resourceType" dict-code="resourceType" />
</ElFormItem>
<ElFormItem prop="resourceName" :label="$t('aiResource.resourceName')">
<ElInput v-model.trim="entity.resourceName" />
</ElFormItem>
<ElFormItem prop="categoryId" :label="$t('aiResource.categoryId')">
<DictSelect
v-model="entity.categoryId"
dict-code="aiResourceCategory"
/>
</ElFormItem>
</ElForm>
<template #footer>
<ElButton @click="closeDialog">
{{ $t('button.cancel') }}
</ElButton>
<ElButton
type="primary"
@click="save"
:loading="btnLoading"
:disabled="btnLoading"
>
{{ $t('button.save') }}
</ElButton>
</template>
</ElDialog>
</template>
<style scoped></style>