Merge remote-tracking branch 'origin/main' into feature/v1.2灵机空间-内容上传审核_rxd

# Conflicts:
#	pnpm-lock.yaml
#	src/components/_base/navbar/index.vue
#	src/components/text-over-tips/index.vue
#	src/layouts/Basic.vue
#	src/layouts/Page.vue
#	src/main.ts
#	src/router/constants.ts
#	src/router/index.ts
#	src/router/typeings.d.ts
#	src/utils/tools.ts
This commit is contained in:
rd
2025-08-14 15:06:46 +08:00
51 changed files with 5928 additions and 152 deletions

View File

@ -1,6 +1,10 @@
<template>
<div class="right-wrap">
<div class="relative mr-12px" @click="setUnread">
<!-- 灵机空间入口 -->
<div class="agent-entry" :class="isAgentRoute ? 'agent' : ''" @click="handleAgentClick"></div>
<!-- 任务中心 -->
<div class="relative mx-16px" @click="setUnread">
<SvgIcon
name="svg-taskCenter"
size="16"
@ -10,6 +14,7 @@
<div class="w-4px h-4px rounded-50% bg-#F64B31 absolute top-1px right-1px" v-if="hasUnreadInfo"></div>
</div>
<!-- 头像设置 -->
<a-dropdown trigger="click" class="layout-avatar-dropdown">
<a-avatar class="cursor-pointer" :size="32">
<img alt="avatar" src="@/assets/avatar.svg" />
@ -83,9 +88,17 @@ import icon1 from '@/assets/option.svg';
import icon2 from '@/assets/exit.svg';
import icon3 from '@/assets/change.svg';
const props = defineProps({
isAgentRoute: {
type: Boolean,
default: false,
},
});
const enterpriseStore = useEnterpriseStore();
const userStore = useUserStore();
const sideBarStore = useSidebarStore();
const route = useRoute();
const hasUnreadInfo = computed(() => sideBarStore.unreadInfo.length);
@ -117,15 +130,13 @@ const setUnread = () => {
sideBarStore.removeTaskUnreadInfo();
}
};
const handleAgentClick = () => {
router.push({ name: props.isAgentRoute ? 'Home' : 'AgentIndex' });
};
</script>
<style scoped lang="scss">
.right-wrap {
display: flex;
padding-right: 20px;
list-style: none;
align-items: center;
}
@import './style.scss';
</style>
<style lang="scss">
.layout-avatar-dropdown,

View File

@ -0,0 +1,27 @@
.right-wrap {
display: flex;
padding-right: 20px;
list-style: none;
align-items: center;
.agent-entry {
width: 100px;
height: 32px;
background: url('@/assets/img/agent/icon-entry.png') 100% 100%;
background-size: cover;
transition: all 0.3s;
cursor: pointer;
&:hover {
background: url('@/assets/img/agent/icon-entry-hover.png') 100% 100%;
background-size: cover;
}
&.agent {
background: url('@/assets/img/agent/icon-home.png') 100% 100%;
background-size: cover;
cursor: pointer;
&:hover {
background: url('@/assets/img/agent/icon-home-hover.png') 100% 100%;
background-size: cover;
}
}
}
}

View File

@ -6,7 +6,7 @@
</div>
</div>
<div class="flex-1">
<NavbarMenu />
<NavbarMenu v-if="!isAgentRoute"/>
</div>
<RightSide :isAgentRoute="isAgentRoute" v-if="userStore.isLogin"/>
</div>

View File

@ -22,7 +22,7 @@
</template>
<script setup>
import { ref, reactive, toRefs, onBeforeMount, onMounted, watchEffect, computed, watch, nextTick } from 'vue';
import { ref, reactive, toRefs, onBeforeMount, onMounted, watchEffect, computed, watch, nextTick, defineProps } from 'vue';
import elementResizeDetectorMaker from 'element-resize-detector';
const props = defineProps({

View File

@ -0,0 +1,137 @@
<template>
<a-upload
:custom-request="customRequest"
action="/"
:limit="limit"
:fileList="fileList"
@change="onChange"
@success="handleSuccess"
@error="handleError"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { Message } from '@arco-design/web-vue';
import { fetchImageUploadFile, fetchUploadFile } from '@/api/all';
import axios from 'axios';
const fileList = ref([]);
const props = defineProps({
modelValue: {
type: [Array, String],
default: '',
},
limit: {
type: Number,
default: 0, // 0 表示不限制
},
});
const emit = defineEmits(['update:modelValue']);
const handleSuccess = (fileItem) => {
let response = fileItem.response;
response = JSON.parse(response);
if (response && response.data.file_url) {
if (props.limit === 1) {
emit('update:modelValue', response.data.file_url);
} else {
emit('update:modelValue', [...props.modelValue, response.data.file_url]);
}
}
};
watch(
() => props.modelValue,
async (value) => {
console.log(value, 'value');
if (value) {
fileList.value =
props.limit == 1
? [
{
name: '',
url: props.modelValue as string,
},
]
: (props.modelValue as string[]).map((item) => {
return {
name: '',
url: item,
};
});
} else {
fileList.value = [];
}
},
{ once: true },
);
let previousFileListLength = 0;
//删除图片
const onChange = (fileList) => {
if (fileList.length < previousFileListLength) {
if (props.limit === 1) {
if (fileList.length === 0) {
emit('update:modelValue', '');
}
} else {
if (fileList.length === 0) {
emit('update:modelValue', []);
} else {
let image_data = fileList.map((item) => item.url);
emit('update:modelValue', image_data);
}
}
}
previousFileListLength = fileList.length;
};
const beforeUpload = (file, files) => {
if (props.limit > 0 && files.length >= props.limit) {
Message.warning(`最多只能上传 ${props.limit} 张图片`);
return false; // 阻止上传
}
return true;
};
const handleError = (error) => {
Message.error('上传失败');
console.error(error);
};
const customRequest = async (option) => {
const { onProgress, onError, onSuccess, fileItem, name } = option;
try {
// 1. 获取预签名上传URL
const response = await fetchUploadFile({ suffix: getFileExtension(fileItem.file.name) });
const preSignedUrl = response?.data?.upload_url;
if (!preSignedUrl) {
throw new Error('未能获取有效的预签名上传地址');
}
console.log('preSignedUrl', preSignedUrl);
// 2. 使用预签名URL上传文件
const blob = new Blob([fileItem.file], { type: fileItem.file.type });
await axios.put(preSignedUrl, blob, {
headers: { 'Content-Type': fileItem.file.type },
});
onSuccess(JSON.stringify(response));
} catch (error) {
onError(error);
}
};
function getFileExtension(filename: string): string {
const match = filename.match(/\.([^.]+)$/);
return match ? match[1].toLowerCase() : '';
}
</script>
<style scoped>
/* 添加一些样式 */
</style>