Commit 504d28a0 by zhangxingmin

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

parents 06f2713e a112d197
......@@ -13,25 +13,29 @@
"preview": "vite preview",
"report": "npm run build --report"
},
"repository": {
"type": "git",
"url": "https://gitee.com/y_project/RuoYi-Vue.git"
},
"dependencies": {
"@ai-sdk/alibaba": "^1.0.17",
"@element-plus/icons-vue": "2.3.1",
"@vueup/vue-quill": "1.2.0",
"@vueuse/core": "13.3.0",
"ai": "^6.0.168",
"ali-oss": "^6.23.0",
"axios": "1.9.0",
"clipboard": "2.0.11",
"dompurify": "^3.3.3",
"echarts": "5.6.0",
"element-plus": "2.9.9",
"file-saver": "2.0.5",
"file-saver": "^2.0.5",
"fuse.js": "6.6.2",
"highlight.js": "^11.11.1",
"js-beautify": "1.14.11",
"js-cookie": "3.0.5",
"jsencrypt": "3.3.2",
"jszip": "^3.10.1",
"lodash-es": "^4.17.21",
"marked": "^16.4.0",
"nprogress": "0.2.0",
"p-limit": "^7.3.0",
"pinia": "3.0.2",
"splitpanes": "^4.0.4",
"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 = [
}
]
},
// {
// path: '/system',
// component: Layout,
// children: [
// {
// path: 'ai',
// name: 'Ai',
// component: Ai,
// meta: {
// title: '银盾AI',
// icon: 'message' // 【关键修改】补充图标名,避免 undefined
// // 如果项目里有其他 svg 图标,换成对应的文件名(不含.svg)
// }
// }
// ]
// },
{
path: '/login',
component: () => import('@/views/login'),
......
import router from '@/router'
import { ElMessageBox, } from 'element-plus'
import { ElMessageBox } from 'element-plus'
import { login, logout, getInfo } from '@/api/login'
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 usePermissionStore from '@/store/modules/permission'
const useUserStore = defineStore(
'user',
{
state: () => ({
token: getToken(),
id: '',
name: '',
nickName: '',
avatar: '',
isSuperAdmin: 0,
roles: [],
permissions: [],
tenants: [], // 新增租户列表
currentTenant: null // 新增当前租户
}),
actions: {
// 登录
login(userInfo) {
const username = userInfo.username.trim()
const password = userInfo.password
const code = userInfo.code
const uuid = userInfo.uuid
return new Promise((resolve, reject) => {
login(username, password, code, uuid).then(res => {
const useUserStore = defineStore('user', {
state: () => ({
token: getToken(),
id: '',
name: '',
nickName: '',
avatar: '',
isSuperAdmin: 0,
roles: [],
permissions: [],
tenants: [], // 新增租户列表
currentTenant: null // 新增当前租户
}),
actions: {
// 登录
login(userInfo) {
const username = userInfo.username.trim()
const password = userInfo.password
const code = userInfo.code
const uuid = userInfo.uuid
return new Promise((resolve, reject) => {
login(username, password, code, uuid)
.then(res => {
setToken(res.data.token)
this.token = res.data.token
resolve()
}).catch(error => {
})
.catch(error => {
reject(error)
})
})
},
// 获取用户信息
getInfo() {
return new Promise((resolve, reject) => {
getInfo().then(res => {
})
},
// 获取用户信息
getInfo() {
return new Promise((resolve, reject) => {
getInfo()
.then(res => {
const user = res.data.apiLoginUserInfoResponse
let avatar = user.avatar || ""
let avatar = user.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 || []
// 尝试从本地存储获取当前租户,否则使用第一个
const savedTenant = JSON.parse(localStorage.getItem('' +
''))
const savedTenant = JSON.parse(localStorage.getItem('' + ''))
const currentTenant = savedTenant || (this.tenants.length > 0 ? this.tenants[0] : null)
if (currentTenant) {
......@@ -74,51 +74,61 @@ const useUserStore = defineStore(
this.nickName = user.nickName
this.avatar = avatar
this.isSuperAdmin = user.isSuperAdmin
console.log('====================================')
console.log('user', user)
console.log('====================================')
/* 初始密码提示 */
if(res.data.isDefaultModifyPwd) {
ElMessageBox.confirm('您的密码还是初始密码,请修改密码!', '安全提示', {
if (res.data.isDefaultModifyPwd) {
ElMessageBox.confirm('您的密码还是初始密码,请修改密码!', '安全提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
router.push({ name: 'Profile', params: { activeTab: 'resetPwd' } })
}).catch(() => {})
})
.then(() => {
router.push({ name: 'Profile', params: { activeTab: 'resetPwd' } })
})
.catch(() => {})
}
/* 过期密码提示 */
if(!res.data.isDefaultModifyPwd && res.isPasswordExpired) {
ElMessageBox.confirm('您的密码已过期,请尽快修改密码!', '安全提示', {
if (!res.data.isDefaultModifyPwd && res.isPasswordExpired) {
ElMessageBox.confirm('您的密码已过期,请尽快修改密码!', '安全提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
router.push({ name: 'Profile', params: { activeTab: 'resetPwd' } })
}).catch(() => {})
})
.then(() => {
router.push({ name: 'Profile', params: { activeTab: 'resetPwd' } })
})
.catch(() => {})
}
resolve(res)
}).catch(error => {
})
.catch(error => {
reject(error)
})
})
},
//切换租户
switchTenant(tenant) {
return new Promise((resolve) => {
this.currentTenant = tenant
localStorage.setItem('currentTenant', JSON.stringify(tenant))
})
},
//切换租户
switchTenant(tenant) {
return new Promise(resolve => {
this.currentTenant = tenant
localStorage.setItem('currentTenant', JSON.stringify(tenant))
// 保存租户ID到专用字段
localStorage.setItem('selectedTenantId', tenant.apiLoginTenantInfoResponse.tenantBizId)
// 保存租户ID到专用字段
localStorage.setItem('selectedTenantId', tenant.apiLoginTenantInfoResponse.tenantBizId)
// 更新角色和权限
if (tenant.roles && tenant.roles.length > 0) {
this.roles = tenant.roles
this.permissions = tenant.permissions
} else {
this.roles = ['ROLE_DEFAULT']
}
// 更新角色和权限
if (tenant.roles && tenant.roles.length > 0) {
this.roles = tenant.roles
this.permissions = tenant.permissions
} else {
this.roles = ['ROLE_DEFAULT']
}
usePermissionStore().generateRoutes().then(accessRoutes => {
usePermissionStore()
.generateRoutes()
.then(accessRoutes => {
// 移除旧路由
const currentRoutes = router.getRoutes()
currentRoutes.forEach(route => {
......@@ -139,35 +149,35 @@ const useUserStore = defineStore(
// window.location.reload() // 确保完全刷新
// })
})
router.push('/workbench')
})
},
// 退出系统
logOut() {
return new Promise((resolve, reject) => {
this.token = ''
this.roles = []
this.permissions = []
this.tenants = []
this.currentTenant = null
removeToken()
localStorage.removeItem('currentTenant')
resolve()
// logout(this.token).then(() => {
// this.token = ''
// this.roles = []
// this.permissions = []
// this.tenants = []
// this.currentTenant = null
// removeToken()
// localStorage.removeItem('currentTenant')
// resolve()
// }).catch(error => {
// reject(error)
// })
})
}
router.push('/workbench')
})
},
// 退出系统
logOut() {
return new Promise((resolve, reject) => {
this.token = ''
this.roles = []
this.permissions = []
this.tenants = []
this.currentTenant = null
removeToken()
localStorage.removeItem('currentTenant')
resolve()
// logout(this.token).then(() => {
// this.token = ''
// this.roles = []
// this.permissions = []
// this.tenants = []
// this.currentTenant = null
// removeToken()
// localStorage.removeItem('currentTenant')
// resolve()
// }).catch(error => {
// reject(error)
// })
})
}
})
}
})
export default useUserStore
......@@ -83,8 +83,8 @@ const router = useRouter()
const { proxy } = getCurrentInstance()
const loginForm = ref({
username: 'admin',
password: '12345',
username: '',
password: '',
rememberMe: false,
code: '',
uuid: ''
......
......@@ -10,6 +10,7 @@
</el-col>
</el-row>
<el-table
height="70%"
:data="tableData"
:span-method="objectSpanMethod"
border
......@@ -52,7 +53,7 @@
: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
label="序号 "
width="55"
......
......@@ -16,7 +16,13 @@
<el-row>
<el-col :span="12">
<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"
filterable
remote
......@@ -35,7 +41,7 @@
:label="item.productName"
:value="item.productBizId"
/>
</el-select>
</el-select> -->
</el-col>
<el-col :span="24" :class="showNameTip ? '' : 'colBottomGap'">
<div class="nameTip">
......@@ -889,6 +895,7 @@ import useUserStore from '@/store/modules/user'
import { ref, computed, watch, nextTick } from 'vue'
import ImageUpload from '@/components/ImageUpload/index.vue' //图片上传组件
import CategoryTable from '@/components/CategoryTable/index.vue' //图片上传组件
const emit = defineEmits(['handleSuccess'])
const props = defineProps({
// 类型,是新增还是编辑,
......@@ -1099,7 +1106,9 @@ const confirmPlatform = () => {
const choosePlatform = async () => {
platFormOpen.value = true
platFormQueryParams.value.pageNo = 1
if (userStore.isSuperAdmin == '0') {
platFormQueryParams.value.loginTenantBizId = userStore.currentTenant.apiLoginTenantInfoResponse.tenantBizId
}
// 获取列表数据
await getPlatFormList()
}
......
......@@ -7,7 +7,7 @@
</div>
</el-col>
</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">
<!-- 动态生成所有列 -->
<template v-for="column in tableColumns" :key="column.prop">
......
......@@ -42,15 +42,15 @@
label="短标题"
prop="shortTitle"
: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">
<dict-tag :options="product_launch_status" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="创建时间" prop="createTime" align="left">
<el-table-column label="创建时间" prop="createTime" align="left" sortable >
<template #default="scope">
{{ formatIsoToDateTime(scope.row.createTime) }}
</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