Commit 504d28a0 by zhangxingmin

Merge remote-tracking branch 'origin/dev' into prod

parents 06f2713e a112d197
...@@ -13,25 +13,29 @@ ...@@ -13,25 +13,29 @@
"preview": "vite preview", "preview": "vite preview",
"report": "npm run build --report" "report": "npm run build --report"
}, },
"repository": {
"type": "git",
"url": "https://gitee.com/y_project/RuoYi-Vue.git"
},
"dependencies": { "dependencies": {
"@ai-sdk/alibaba": "^1.0.17",
"@element-plus/icons-vue": "2.3.1", "@element-plus/icons-vue": "2.3.1",
"@vueup/vue-quill": "1.2.0", "@vueup/vue-quill": "1.2.0",
"@vueuse/core": "13.3.0", "@vueuse/core": "13.3.0",
"ai": "^6.0.168",
"ali-oss": "^6.23.0",
"axios": "1.9.0", "axios": "1.9.0",
"clipboard": "2.0.11", "clipboard": "2.0.11",
"dompurify": "^3.3.3",
"echarts": "5.6.0", "echarts": "5.6.0",
"element-plus": "2.9.9", "element-plus": "2.9.9",
"file-saver": "2.0.5", "file-saver": "^2.0.5",
"fuse.js": "6.6.2", "fuse.js": "6.6.2",
"highlight.js": "^11.11.1",
"js-beautify": "1.14.11", "js-beautify": "1.14.11",
"js-cookie": "3.0.5", "js-cookie": "3.0.5",
"jsencrypt": "3.3.2", "jsencrypt": "3.3.2",
"jszip": "^3.10.1",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"marked": "^16.4.0",
"nprogress": "0.2.0", "nprogress": "0.2.0",
"p-limit": "^7.3.0",
"pinia": "3.0.2", "pinia": "3.0.2",
"splitpanes": "^4.0.4", "splitpanes": "^4.0.4",
"vue": "3.5.16", "vue": "3.5.16",
......
// src/api/ai/ai.js
import request from '@/utils/request'
import { getToken } from '@/utils/auth'
import useUserStore from '@/store/modules/user'
// 加载随机词条列表
export function randList(data) {
return request({
url: '/ai/api/entry/rand/list',
method: 'post',
data: data
})
}
/**
* 获取完整回答(非流式)
*/
export async function getFullAnswer(question, timeout = 300000) {
const userStore = useUserStore();
const token = getToken();
const tenantId = userStore.currentTenant?.apiLoginTenantInfoResponse?.tenantBizId;
const headers = {
'Authorization': `Bearer ${token}`
};
if (tenantId) {
headers['X-Tenant-ID'] = tenantId;
}
const baseUrl = import.meta.env.VITE_APP_BASE_API;
const url = `${baseUrl}/ai/api/api/ai/stream?question=${encodeURIComponent(question)}`;
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(url, {
method: 'GET',
headers,
signal: controller.signal
});
clearTimeout(timeoutId);
if (!response.ok) {
let errorMessage = response.statusText;
try {
const errorBody = await response.json();
errorMessage = errorBody.msg || errorBody.message || errorMessage;
} catch (e) {}
throw new Error(errorMessage);
}
// 直接返回纯文本
return await response.text();
} finally {
clearTimeout(timeoutId);
}
}
/**
* 查询输出流信息(流式响应)
* 增强错误处理:当HTTP状态非2xx时,解析错误响应体并抛出包含code属性的错误
*/
export function getStream(question, timeout = 300000) {
const userStore = useUserStore()
const token = getToken()
const tenantId = userStore.currentTenant?.apiLoginTenantInfoResponse?.tenantBizId
const headers = {
'Accept': 'text/event-stream',
'Authorization': `Bearer ${token}`
}
if (tenantId) {
headers['X-Tenant-ID'] = tenantId
}
const baseUrl = import.meta.env.VITE_APP_BASE_API
const url = `${baseUrl}/ai/api/api/ai/stream-sse?question=${encodeURIComponent(question)}`;
const controller = new AbortController()
const timeoutId = setTimeout(() => controller.abort(), timeout)
return fetch(url, {
method: 'GET',
headers,
signal: controller.signal
}).then(async (response) => {
console.log(response)
clearTimeout(timeoutId)
if (!response.ok) {
// 尝试解析错误响应体
let errorCode = null
let errorMessage = response.statusText
try {
const errorBody = await response.json()
if (errorBody && errorBody.code !== undefined) {
errorCode = errorBody.code
errorMessage = errorBody.msg || errorBody.message || errorMessage
}
} catch (e) {
// 无法解析JSON,使用默认错误信息
}
const error = new Error(errorMessage)
error.code = errorCode
error.status = response.status
throw error
}
return response
}).finally(() => {
clearTimeout(timeoutId)
})
}
/**
* 获取服务卡片列表(产品列表)
*/
export async function getServiceCardList() {
const userStore = useUserStore()
const token = getToken()
const userId = userStore.userInfo?.userId || userStore.userId || '30'
let cleanToken = token
if (token && token.startsWith('Bearer ')) {
cleanToken = token.slice(7)
}
const headers = {
'Content-Type': 'application/json',
'x-authorization': `sfpfamilyfinancialplanning ${cleanToken}`
}
const url = 'https://hoservice.ydhomeoffice.cn/hoserviceApi/ydServiceCard/serviceCardList'
const body = {
userId: String(userId),
cardType: '11'
}
try {
const response = await fetch(url, {
method: 'POST',
headers: headers,
body: JSON.stringify(body)
})
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
}
const data = await response.json()
return data
} catch (error) {
console.error('获取服务卡片列表失败:', error)
throw error
}
}
/**
* 启动流式生成,返回 sessionId
*/
export async function startStream(question) {
const userStore = useUserStore();
const token = getToken();
const tenantId = userStore.currentTenant?.apiLoginTenantInfoResponse?.tenantBizId;
const headers = {
'Authorization': `Bearer ${token}`
};
if (tenantId) {
headers['X-Tenant-ID'] = tenantId;
}
const baseUrl = import.meta.env.VITE_APP_BASE_API;
const url = `${baseUrl}/ai/api/api/ai/start-stream`;
const formData = new FormData();
formData.append('question', question);
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 30000); // 30秒超时
try {
const response = await fetch(url, {
method: 'POST',
headers,
body: formData,
signal: controller.signal
});
clearTimeout(timeoutId);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
return data.sessionId;
} catch (error) {
clearTimeout(timeoutId);
throw error;
}
}
/**
* 轮询获取流式内容
*/
export async function pollContent(sessionId) {
const userStore = useUserStore();
const token = getToken();
const tenantId = userStore.currentTenant?.apiLoginTenantInfoResponse?.tenantBizId;
const headers = {
'Authorization': `Bearer ${token}`
};
if (tenantId) {
headers['X-Tenant-ID'] = tenantId;
}
const baseUrl = import.meta.env.VITE_APP_BASE_API;
const url = `${baseUrl}/ai/api/api/ai/stream-content?sessionId=${encodeURIComponent(sessionId)}`;
const response = await fetch(url, { headers });
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return await response.json(); // { content: "...", finished: true/false }
}
\ No newline at end of file
...@@ -37,6 +37,22 @@ export const constantRoutes = [ ...@@ -37,6 +37,22 @@ export const constantRoutes = [
} }
] ]
}, },
// {
// path: '/system',
// component: Layout,
// children: [
// {
// path: 'ai',
// name: 'Ai',
// component: Ai,
// meta: {
// title: '银盾AI',
// icon: 'message' // 【关键修改】补充图标名,避免 undefined
// // 如果项目里有其他 svg 图标,换成对应的文件名(不含.svg)
// }
// }
// ]
// },
{ {
path: '/login', path: '/login',
component: () => import('@/views/login'), component: () => import('@/views/login'),
......
import router from '@/router' import router from '@/router'
import { ElMessageBox, } from 'element-plus' import { ElMessageBox } from 'element-plus'
import { login, logout, getInfo } from '@/api/login' import { login, logout, getInfo } from '@/api/login'
import { getToken, setToken, removeToken } from '@/utils/auth' import { getToken, setToken, removeToken } from '@/utils/auth'
import { isHttp, isEmpty } from "@/utils/validate" import { isHttp, isEmpty } from '@/utils/validate'
import defAva from '@/assets/images/profile.jpg' import defAva from '@/assets/images/profile.jpg'
import usePermissionStore from '@/store/modules/permission' import usePermissionStore from '@/store/modules/permission'
const useUserStore = defineStore( const useUserStore = defineStore('user', {
'user',
{
state: () => ({ state: () => ({
token: getToken(), token: getToken(),
id: '', id: '',
...@@ -29,11 +27,13 @@ const useUserStore = defineStore( ...@@ -29,11 +27,13 @@ const useUserStore = defineStore(
const code = userInfo.code const code = userInfo.code
const uuid = userInfo.uuid const uuid = userInfo.uuid
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
login(username, password, code, uuid).then(res => { login(username, password, code, uuid)
.then(res => {
setToken(res.data.token) setToken(res.data.token)
this.token = res.data.token this.token = res.data.token
resolve() resolve()
}).catch(error => { })
.catch(error => {
reject(error) reject(error)
}) })
}) })
...@@ -41,19 +41,19 @@ const useUserStore = defineStore( ...@@ -41,19 +41,19 @@ const useUserStore = defineStore(
// 获取用户信息 // 获取用户信息
getInfo() { getInfo() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
getInfo().then(res => { getInfo()
.then(res => {
const user = res.data.apiLoginUserInfoResponse const user = res.data.apiLoginUserInfoResponse
let avatar = user.avatar || "" let avatar = user.avatar || ''
if (!isHttp(avatar)) { if (!isHttp(avatar)) {
avatar = (isEmpty(avatar)) ? defAva : import.meta.env.VITE_APP_BASE_API + avatar avatar = isEmpty(avatar) ? defAva : import.meta.env.VITE_APP_BASE_API + avatar
} }
// 设置租户列表 // 设置租户列表
this.tenants = res.data.apiLoginTenantResponseList || [] this.tenants = res.data.apiLoginTenantResponseList || []
// 尝试从本地存储获取当前租户,否则使用第一个 // 尝试从本地存储获取当前租户,否则使用第一个
const savedTenant = JSON.parse(localStorage.getItem('' + const savedTenant = JSON.parse(localStorage.getItem('' + ''))
''))
const currentTenant = savedTenant || (this.tenants.length > 0 ? this.tenants[0] : null) const currentTenant = savedTenant || (this.tenants.length > 0 ? this.tenants[0] : null)
if (currentTenant) { if (currentTenant) {
...@@ -74,36 +74,44 @@ const useUserStore = defineStore( ...@@ -74,36 +74,44 @@ const useUserStore = defineStore(
this.nickName = user.nickName this.nickName = user.nickName
this.avatar = avatar this.avatar = avatar
this.isSuperAdmin = user.isSuperAdmin this.isSuperAdmin = user.isSuperAdmin
console.log('====================================')
console.log('user', user)
console.log('====================================')
/* 初始密码提示 */ /* 初始密码提示 */
if(res.data.isDefaultModifyPwd) { if (res.data.isDefaultModifyPwd) {
ElMessageBox.confirm('您的密码还是初始密码,请修改密码!', '安全提示', { ElMessageBox.confirm('您的密码还是初始密码,请修改密码!', '安全提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
}).then(() => { })
.then(() => {
router.push({ name: 'Profile', params: { activeTab: 'resetPwd' } }) router.push({ name: 'Profile', params: { activeTab: 'resetPwd' } })
}).catch(() => {}) })
.catch(() => {})
} }
/* 过期密码提示 */ /* 过期密码提示 */
if(!res.data.isDefaultModifyPwd && res.isPasswordExpired) { if (!res.data.isDefaultModifyPwd && res.isPasswordExpired) {
ElMessageBox.confirm('您的密码已过期,请尽快修改密码!', '安全提示', { ElMessageBox.confirm('您的密码已过期,请尽快修改密码!', '安全提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
}).then(() => { })
.then(() => {
router.push({ name: 'Profile', params: { activeTab: 'resetPwd' } }) router.push({ name: 'Profile', params: { activeTab: 'resetPwd' } })
}).catch(() => {}) })
.catch(() => {})
} }
resolve(res) resolve(res)
}).catch(error => { })
.catch(error => {
reject(error) reject(error)
}) })
}) })
}, },
//切换租户 //切换租户
switchTenant(tenant) { switchTenant(tenant) {
return new Promise((resolve) => { return new Promise(resolve => {
this.currentTenant = tenant this.currentTenant = tenant
localStorage.setItem('currentTenant', JSON.stringify(tenant)) localStorage.setItem('currentTenant', JSON.stringify(tenant))
...@@ -118,7 +126,9 @@ const useUserStore = defineStore( ...@@ -118,7 +126,9 @@ const useUserStore = defineStore(
this.roles = ['ROLE_DEFAULT'] this.roles = ['ROLE_DEFAULT']
} }
usePermissionStore().generateRoutes().then(accessRoutes => { usePermissionStore()
.generateRoutes()
.then(accessRoutes => {
// 移除旧路由 // 移除旧路由
const currentRoutes = router.getRoutes() const currentRoutes = router.getRoutes()
currentRoutes.forEach(route => { currentRoutes.forEach(route => {
...@@ -168,6 +178,6 @@ const useUserStore = defineStore( ...@@ -168,6 +178,6 @@ const useUserStore = defineStore(
}) })
} }
} }
}) })
export default useUserStore export default useUserStore
...@@ -83,8 +83,8 @@ const router = useRouter() ...@@ -83,8 +83,8 @@ const router = useRouter()
const { proxy } = getCurrentInstance() const { proxy } = getCurrentInstance()
const loginForm = ref({ const loginForm = ref({
username: 'admin', username: '',
password: '12345', password: '',
rememberMe: false, rememberMe: false,
code: '', code: '',
uuid: '' uuid: ''
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
</el-col> </el-col>
</el-row> </el-row>
<el-table <el-table
height="70%"
:data="tableData" :data="tableData"
:span-method="objectSpanMethod" :span-method="objectSpanMethod"
border border
...@@ -52,7 +53,7 @@ ...@@ -52,7 +53,7 @@
:close-on-click-modal="false" :close-on-click-modal="false"
> >
<!-- 表格数据 --> <!-- 表格数据 -->
<el-table v-loading="settingLoading" :data="settingList" border ref="settingTableRef"> <el-table v-loading="settingLoading" :data="settingList" border ref="settingTableRef" height="75%">
<el-table-column <el-table-column
label="序号 " label="序号 "
width="55" width="55"
......
...@@ -16,7 +16,13 @@ ...@@ -16,7 +16,13 @@
<el-row> <el-row>
<el-col :span="12"> <el-col :span="12">
<div class="commonHeader">产品标题</div> <div class="commonHeader">产品标题</div>
<el-select <el-input
v-model="form['apiProductLaunchDto'].title"
placeholder="请输入"
maxlength="20"
show-word-limit
/>
<!-- <el-select
v-model="form['apiProductLaunchDto'].title" v-model="form['apiProductLaunchDto'].title"
filterable filterable
remote remote
...@@ -35,7 +41,7 @@ ...@@ -35,7 +41,7 @@
:label="item.productName" :label="item.productName"
:value="item.productBizId" :value="item.productBizId"
/> />
</el-select> </el-select> -->
</el-col> </el-col>
<el-col :span="24" :class="showNameTip ? '' : 'colBottomGap'"> <el-col :span="24" :class="showNameTip ? '' : 'colBottomGap'">
<div class="nameTip"> <div class="nameTip">
...@@ -889,6 +895,7 @@ import useUserStore from '@/store/modules/user' ...@@ -889,6 +895,7 @@ import useUserStore from '@/store/modules/user'
import { ref, computed, watch, nextTick } from 'vue' import { ref, computed, watch, nextTick } from 'vue'
import ImageUpload from '@/components/ImageUpload/index.vue' //图片上传组件 import ImageUpload from '@/components/ImageUpload/index.vue' //图片上传组件
import CategoryTable from '@/components/CategoryTable/index.vue' //图片上传组件 import CategoryTable from '@/components/CategoryTable/index.vue' //图片上传组件
const emit = defineEmits(['handleSuccess']) const emit = defineEmits(['handleSuccess'])
const props = defineProps({ const props = defineProps({
// 类型,是新增还是编辑, // 类型,是新增还是编辑,
...@@ -1099,7 +1106,9 @@ const confirmPlatform = () => { ...@@ -1099,7 +1106,9 @@ const confirmPlatform = () => {
const choosePlatform = async () => { const choosePlatform = async () => {
platFormOpen.value = true platFormOpen.value = true
platFormQueryParams.value.pageNo = 1 platFormQueryParams.value.pageNo = 1
if (userStore.isSuperAdmin == '0') {
platFormQueryParams.value.loginTenantBizId = userStore.currentTenant.apiLoginTenantInfoResponse.tenantBizId
}
// 获取列表数据 // 获取列表数据
await getPlatFormList() await getPlatFormList()
} }
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
</div> </div>
</el-col> </el-col>
</el-row> </el-row>
<el-table :data="tableData" :span-method="objectSpanMethod" border style="width: 100%; margin-top: 20px" <el-table :data="tableData" :span-method="objectSpanMethod" border style="width: 100%; margin-top: 20px" height="70%"
row-key="rowKey"> row-key="rowKey">
<!-- 动态生成所有列 --> <!-- 动态生成所有列 -->
<template v-for="column in tableColumns" :key="column.prop"> <template v-for="column in tableColumns" :key="column.prop">
......
...@@ -42,15 +42,15 @@ ...@@ -42,15 +42,15 @@
label="短标题" label="短标题"
prop="shortTitle" prop="shortTitle"
:show-overflow-tooltip="true" :show-overflow-tooltip="true"
width="150" width="200"
/> />
<el-table-column label="状态" prop="status" align="left"> <el-table-column label="状态" prop="status" align="left" width="100">
<template #default="scope"> <template #default="scope">
<dict-tag :options="product_launch_status" :value="scope.row.status" /> <dict-tag :options="product_launch_status" :value="scope.row.status" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="创建时间" prop="createTime" align="left"> <el-table-column label="创建时间" prop="createTime" align="left" sortable >
<template #default="scope"> <template #default="scope">
{{ formatIsoToDateTime(scope.row.createTime) }} {{ formatIsoToDateTime(scope.row.createTime) }}
</template> </template>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment