Compare commits
21 Commits
7670a92f4b
...
a0cab6f763
Author | SHA1 | Date | |
---|---|---|---|
a0cab6f763 | |||
f9b959f3d4 | |||
c078fa081e | |||
ce692f2e6e | |||
7c90333fcf | |||
68c5ffdb64 | |||
6296bc5e19 | |||
af586485f0 | |||
550a35effc | |||
|
5482bbcbeb | ||
|
fe47950a7c | ||
|
8d3b8d3b3c | ||
|
185d04bc32 | ||
|
316e8101c3 | ||
|
9f24fec9d6 | ||
|
065d1b556f | ||
|
8a636bfde3 | ||
|
35bc2f99e6 | ||
|
29bdcc1914 | ||
|
70625f6ea1 | ||
|
258eb17281 |
@ -54,5 +54,6 @@ module.exports = {
|
||||
'scss/comment-no-empty': null,
|
||||
'selector-class-pattern': null,
|
||||
'font-family-no-missing-generic-family-keyword': null,
|
||||
'declaration-property-value-no-unknown': null,
|
||||
},
|
||||
}
|
||||
|
14
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Debug h5",
|
||||
"type": "chrome",
|
||||
"runtimeArgs": ["--remote-debugging-port=9222"],
|
||||
"request": "launch",
|
||||
"url": "http://localhost:5173",
|
||||
"webRoot": "${workspaceFolder}",
|
||||
"preLaunchTask": "uni:h5"
|
||||
}
|
||||
]
|
||||
}
|
16
.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "uni:h5",
|
||||
"type": "npm",
|
||||
"script": "dev --devtools",
|
||||
"isBackground": true,
|
||||
"problemMatcher": "$vite",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
<p align="center">
|
||||
<a href="https://github.com/feige996/unibest">
|
||||
<img width="160" src="./src/static/logo.svg">
|
||||
<img width="160" src="./src/static/logo.png">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
12
env/.env
vendored
@ -1,14 +1,14 @@
|
||||
VITE_APP_TITLE = 'unibest'
|
||||
VITE_APP_TITLE = 'aixiaomo'
|
||||
VITE_APP_PORT = 9000
|
||||
|
||||
VITE_UNI_APPID = 'H57F2ACE4'
|
||||
VITE_WX_APPID = 'wxa2abb91f64032a2b'
|
||||
|
||||
VITE_DEV_TOKEN= ""
|
||||
# h5部署网站的base,配置到 manifest.config.ts 里的 h5.router.base
|
||||
VITE_APP_PUBLIC_BASE=/
|
||||
|
||||
VITE_SERVER_BASEURL = 'https://ukw0y1.laf.run'
|
||||
VITE_UPLOAD_BASEURL = 'https://ukw0y1.laf.run/upload'
|
||||
VITE_SERVER_BASEURL = 'http://114.218.158.24:9020'
|
||||
VITE_UPLOAD_BASEURL = 'http://114.218.158.24:9020'
|
||||
|
||||
# 有些同学可能需要在微信小程序里面根据 develop、trial、release 分别设置上传地址,参考代码如下。
|
||||
# 下面的变量如果没有设置,会默认使用 VITE_SERVER_BASEURL or VITE_UPLOAD_BASEURL
|
||||
@ -21,5 +21,5 @@ VITE_UPLOAD_BASEURL__WEIXIN_TRIAL = 'https://ukw0y1.laf.run/upload'
|
||||
VITE_UPLOAD_BASEURL__WEIXIN_RELEASE = 'https://ukw0y1.laf.run/upload'
|
||||
|
||||
# h5是否需要配置代理
|
||||
VITE_APP_PROXY=false
|
||||
VITE_APP_PROXY_PREFIX = '/api'
|
||||
VITE_APP_PROXY=true
|
||||
VITE_APP_PROXY_PREFIX = '/upload'
|
||||
|
4
env/.env.development
vendored
@ -1,6 +1,8 @@
|
||||
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
|
||||
NODE_ENV = 'development'
|
||||
NODE_ENV = 'dev'
|
||||
# 是否去除console 和 debugger
|
||||
VITE_DELETE_CONSOLE = false
|
||||
# 是否开启sourcemap
|
||||
VITE_SHOW_SOURCEMAP = true
|
||||
|
||||
VITE_SERVER_BASEURL = 'http://114.218.158.24:9020'
|
6
env/.env.production
vendored
@ -1,6 +1,10 @@
|
||||
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
|
||||
NODE_ENV = 'development'
|
||||
NODE_ENV = 'prod'
|
||||
# 是否去除console 和 debugger
|
||||
VITE_DELETE_CONSOLE = true
|
||||
# 是否开启sourcemap
|
||||
VITE_SHOW_SOURCEMAP = false
|
||||
|
||||
VITE_SERVER_BASEURL = 'https://erpapi.fontree.cn' # 体制外 OA
|
||||
|
||||
|
||||
|
3
env/.env.test
vendored
@ -1,4 +1,5 @@
|
||||
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
|
||||
NODE_ENV = 'development'
|
||||
NODE_ENV = 'test'
|
||||
# 是否去除console 和 debugger
|
||||
VITE_DELETE_CONSOLE = false
|
||||
VITE_SERVER_BASEURL = 'http://114.218.158.24:9020'
|
||||
|
@ -102,6 +102,7 @@
|
||||
"@tanstack/vue-query": "^5.62.16",
|
||||
"abortcontroller-polyfill": "^1.7.8",
|
||||
"dayjs": "1.11.10",
|
||||
"element-plus": "^2.9.10",
|
||||
"pinia": "2.0.36",
|
||||
"pinia-plugin-persistedstate": "3.2.1",
|
||||
"qs": "6.5.3",
|
||||
|
@ -2,8 +2,8 @@ import { defineUniPages } from '@uni-helper/vite-plugin-uni-pages'
|
||||
|
||||
export default defineUniPages({
|
||||
globalStyle: {
|
||||
navigationStyle: 'default',
|
||||
navigationBarTitleText: 'unibest',
|
||||
navigationStyle: 'custom',
|
||||
navigationBarTitleText: '',
|
||||
navigationBarBackgroundColor: '#f8f8f8',
|
||||
navigationBarTextStyle: 'black',
|
||||
backgroundColor: '#FFFFFF',
|
||||
@ -21,23 +21,10 @@ export default defineUniPages({
|
||||
selectedColor: '#018d71',
|
||||
backgroundColor: '#F8F8F8',
|
||||
borderStyle: 'black',
|
||||
height: '50px',
|
||||
fontSize: '10px',
|
||||
height: '0px',
|
||||
fontSize: '0px',
|
||||
iconWidth: '24px',
|
||||
spacing: '3px',
|
||||
list: [
|
||||
{
|
||||
iconPath: 'static/tabbar/home.png',
|
||||
selectedIconPath: 'static/tabbar/homeHL.png',
|
||||
pagePath: 'pages/index/index',
|
||||
text: '首页',
|
||||
},
|
||||
{
|
||||
iconPath: 'static/tabbar/example.png',
|
||||
selectedIconPath: 'static/tabbar/exampleHL.png',
|
||||
pagePath: 'pages/about/about',
|
||||
text: '关于',
|
||||
},
|
||||
],
|
||||
list: [],
|
||||
},
|
||||
})
|
||||
|
14553
pnpm-lock.yaml
15
src/App.vue
@ -1,6 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { onLaunch, onShow, onHide } from '@dcloudio/uni-app'
|
||||
import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only'
|
||||
const { statusBarHeight } = useStatus()
|
||||
import { useStatus } from '@/store/status'
|
||||
|
||||
onLaunch(() => {
|
||||
console.log('App Launch')
|
||||
@ -18,7 +20,18 @@ onHide(() => {
|
||||
button::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
/*解决阅览图片关闭按钮会显示在状态栏区域的问题*/
|
||||
#u-a-p > div > div {
|
||||
margin-top: var(--statusBarHeight);
|
||||
}
|
||||
/*不显示滚动条的类*/
|
||||
.no-scroll {
|
||||
-ms-overflow-style: none; /* IE 和 Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
.no-scroll::-webkit-scrollbar {
|
||||
display: none; /* Webkit 浏览器 */
|
||||
}
|
||||
swiper,
|
||||
scroll-view {
|
||||
flex: 1;
|
||||
|
@ -47,7 +47,7 @@ const httpInterceptor = {
|
||||
options.timeout = 10000 // 10s
|
||||
// 2. (可选)添加小程序端请求头标识
|
||||
options.header = {
|
||||
platform, // 可选,与 uniapp 定义的平台一致,告诉后台来源
|
||||
// platform, // 可选,与 uniapp 定义的平台一致,告诉后台来源
|
||||
...options.header,
|
||||
}
|
||||
// 3. 添加 token 请求头标识
|
||||
|
@ -2,10 +2,15 @@ import '@/style/index.scss'
|
||||
import { VueQueryPlugin } from '@tanstack/vue-query'
|
||||
import 'virtual:uno.css'
|
||||
import { createSSRApp } from 'vue'
|
||||
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
|
||||
import ElementPlus from 'element-plus'
|
||||
import 'element-plus/dist/index.css'
|
||||
|
||||
import App from './App.vue'
|
||||
import { prototypeInterceptor, requestInterceptor, routeInterceptor } from './interceptors'
|
||||
import store from './store'
|
||||
// import VConsole from 'vconsole'
|
||||
// new VConsole()
|
||||
|
||||
export function createApp() {
|
||||
const app = createSSRApp(App)
|
||||
@ -14,6 +19,9 @@ export function createApp() {
|
||||
app.use(requestInterceptor)
|
||||
app.use(prototypeInterceptor)
|
||||
app.use(VueQueryPlugin)
|
||||
app.use(ElementPlus, {
|
||||
locale: zhCn,
|
||||
})
|
||||
|
||||
return {
|
||||
app,
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "unibest",
|
||||
"name": "aixiaomo",
|
||||
"appid": "H57F2ACE4",
|
||||
"description": "",
|
||||
"versionName": "1.0.0",
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"globalStyle": {
|
||||
"navigationStyle": "default",
|
||||
"navigationBarTitleText": "unibest",
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "",
|
||||
"navigationBarBackgroundColor": "#f8f8f8",
|
||||
"navigationBarTextStyle": "black",
|
||||
"backgroundColor": "#FFFFFF"
|
||||
@ -18,32 +18,19 @@
|
||||
"selectedColor": "#018d71",
|
||||
"backgroundColor": "#F8F8F8",
|
||||
"borderStyle": "black",
|
||||
"height": "50px",
|
||||
"fontSize": "10px",
|
||||
"height": "0px",
|
||||
"fontSize": "0px",
|
||||
"iconWidth": "24px",
|
||||
"spacing": "3px",
|
||||
"list": [
|
||||
{
|
||||
"iconPath": "static/tabbar/home.png",
|
||||
"selectedIconPath": "static/tabbar/homeHL.png",
|
||||
"pagePath": "pages/index/index",
|
||||
"text": "首页"
|
||||
},
|
||||
{
|
||||
"iconPath": "static/tabbar/example.png",
|
||||
"selectedIconPath": "static/tabbar/exampleHL.png",
|
||||
"pagePath": "pages/about/about",
|
||||
"text": "关于"
|
||||
}
|
||||
]
|
||||
"list": []
|
||||
},
|
||||
"pages": [
|
||||
{
|
||||
"path": "pages/index/index",
|
||||
"type": "home",
|
||||
"layout": "default",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "首页"
|
||||
"navigationBarHidden": true
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -52,6 +39,14 @@
|
||||
"style": {
|
||||
"navigationBarTitleText": "关于"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/preview/index",
|
||||
"type": "page"
|
||||
},
|
||||
{
|
||||
"path": "pages/webview/index",
|
||||
"type": "page"
|
||||
}
|
||||
],
|
||||
"subPackages": []
|
||||
|
52
src/pages/index/utils/api.js
Normal file
@ -0,0 +1,52 @@
|
||||
import { getEnvBaseUrl } from '@/utils'
|
||||
import { httpPost } from '@/utils/http'
|
||||
// import { TOKEN } from './test';
|
||||
import { apis, uploadFile } from '@/utils/tools'
|
||||
const baseUrl = getEnvBaseUrl()
|
||||
|
||||
// /artwork/get-chunk-list 获取文件分断数据
|
||||
// /artwork/upload-chunk 分断上传画作图片
|
||||
export const uploadFiles = (url, params) => {
|
||||
let token = uni.getStorageSync('authorization')
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.uploadFile({
|
||||
url: baseUrl + url,
|
||||
filePath: params.tempFilePath,
|
||||
name: 'Chunk',
|
||||
formData: params.formData,
|
||||
header: {
|
||||
authorization: token,
|
||||
},
|
||||
complete: (res) => {
|
||||
// console.log('res: ',res);
|
||||
if (res.statusCode == 200) {
|
||||
resolve(res)
|
||||
} else {
|
||||
reject(res)
|
||||
}
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
const getChunkList = (file) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
url: baseUrl + '/artwork/get-chunk-list',
|
||||
data: file,
|
||||
header: {
|
||||
'Content-Type': 'application/json', //"multipart/form-data"
|
||||
},
|
||||
success(res) {
|
||||
resolve(res)
|
||||
},
|
||||
fail(rej) {
|
||||
reject(Jrej)
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export const uploadFileChunk = (data) => {
|
||||
let token = uni.getStorageSync('authorization')
|
||||
return uploadFiles('/artwork/upload-chunk', data)
|
||||
}
|
205
src/pages/index/utils/data.js
Normal file
@ -0,0 +1,205 @@
|
||||
// 发送消息
|
||||
async function sendText1(msgData = '') {
|
||||
// let msgData=''
|
||||
// if(msg===''){
|
||||
// msgData=msg
|
||||
// }else if(msg.detail && msg.detail.value){
|
||||
|
||||
// msgData=msg.detail.value
|
||||
// }
|
||||
// console.log('msgData',msg)
|
||||
// uni.showToast({ title: inputText.value, icon: 'error' })
|
||||
|
||||
msgLoading.value = true
|
||||
const text = inputText.value.trim()
|
||||
const dataBlo = text //toRaw(msgData)
|
||||
console.log('dataBlo', dataBlo)
|
||||
if (!text) {
|
||||
msgLoading.value = false
|
||||
|
||||
uni.showToast({
|
||||
title: '请输入信息',
|
||||
icon: 'error'
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
if (loading.value) return
|
||||
|
||||
inputText.value = ''
|
||||
loading.value = true
|
||||
const tempUploadList = Object.assign([], uploadList)
|
||||
let fileList: any[] = []
|
||||
if (tempUploadList.length > 0) {
|
||||
fileList = tempUploadList.map((item: UploadFile) => {
|
||||
if (item.uploadFileType === uploadFileTypeEm.image) {
|
||||
// 图片格式
|
||||
return {
|
||||
type: 'image_url',
|
||||
image_url: {
|
||||
url: item.url,
|
||||
},
|
||||
}
|
||||
} else if (item.uploadFileType === uploadFileTypeEm.video) {
|
||||
console.log(item, '====item=====')
|
||||
// 视频格式(改成和图片一样的结构)
|
||||
return {
|
||||
type: 'video_url',
|
||||
video_url: {
|
||||
url: item.ori_url,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
// 其他文件类型保持原样
|
||||
const fileType = `${item.uploadFileType}_url`
|
||||
return {
|
||||
type: fileType,
|
||||
[fileType]: item.url,
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 添加用户文本消息
|
||||
addMessage({
|
||||
role: 'user',
|
||||
type: 'text',
|
||||
content: text,
|
||||
timestamp: new Date(),
|
||||
})
|
||||
|
||||
//图片、视频、文件分开发送
|
||||
if (tempUploadList.length > 0) {
|
||||
Object.values(uploadFileTypeEm).forEach((item: any) => {
|
||||
if (tempUploadList.find((v: UploadFile) => v.uploadFileType === item)) {
|
||||
addMessage({
|
||||
role: 'user',
|
||||
type: item,
|
||||
content: tempUploadList.filter((v) => v.uploadFileType === item),
|
||||
timestamp: new Date(),
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
//更新上下文消息
|
||||
historyUserMsgs.push({
|
||||
role: 'user',
|
||||
content: [{
|
||||
type: 'text',
|
||||
text
|
||||
}, ...fileList],
|
||||
timestamp: new Date(),
|
||||
})
|
||||
|
||||
//显示更多图片遮罩
|
||||
const showMoreImgMask = tempUploadList.filter((v: UploadFile) => v.uploadFileType === uploadFileTypeEm.image).length > 4
|
||||
showImageMask.value = showMoreImgMask
|
||||
} else {
|
||||
//更新上下文消息
|
||||
historyUserMsgs.push({
|
||||
role: 'user',
|
||||
content: text,
|
||||
timestamp: new Date(),
|
||||
})
|
||||
}
|
||||
|
||||
// AI消息
|
||||
// const aiMsg: IMessage = {
|
||||
const aiMsg = {
|
||||
role: 'assistant',
|
||||
type: 'text',
|
||||
content: inputText.value,
|
||||
timestamp: new Date(),
|
||||
}
|
||||
addMessage(aiMsg)
|
||||
|
||||
//清除上传列表
|
||||
uploadList.splice(0, uploadList.length)
|
||||
// console.log('[msgData] : historyUserMsgs: ',msgData);
|
||||
// console.log('[msgData] : historyUserMsgs: ', historyUserMsgs);
|
||||
const body: IGptRequestBody = {
|
||||
model: chatMode.value,
|
||||
max_tokens: 1000,
|
||||
temperature: 1,
|
||||
listUuid: listUuid.value,
|
||||
top_p: 1,
|
||||
presence_penalty: 0,
|
||||
frequency_penalty: 0,
|
||||
messages: historyUserMsgs, // text ? [aiMsg] : historyUserMsgs,
|
||||
stream: true,
|
||||
}
|
||||
|
||||
try {
|
||||
// aiMsg.content = ''
|
||||
// 发送问题到后端
|
||||
const resp = await fetch(baseUrl + '/chat/app/completion', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: token.value
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
})
|
||||
|
||||
const reader = resp.body!.getReader()
|
||||
const decoder = new TextDecoder()
|
||||
let buffer = ''
|
||||
let done = false
|
||||
|
||||
while (!done) {
|
||||
const {
|
||||
value,
|
||||
done: streamDone
|
||||
} = await reader.read()
|
||||
done = streamDone
|
||||
if (value) {
|
||||
buffer += decoder.decode(value, {
|
||||
stream: true
|
||||
})
|
||||
const lines = buffer.split(/\r?\n/)
|
||||
buffer = lines.pop() !
|
||||
|
||||
for (const line of lines) {
|
||||
// 只处理以 "data: " 开头的行
|
||||
if (!line.startsWith('data: ')) continue
|
||||
const chunk = line.slice(6).trim()
|
||||
|
||||
if (chunk === '[DONE]') {
|
||||
done = true
|
||||
console.log('sss')
|
||||
break
|
||||
}
|
||||
try {
|
||||
const json = JSON.parse(chunk)
|
||||
const delta = json.choices?.[0]?.delta?.content
|
||||
|
||||
if (delta) {
|
||||
msgLoading.value = false
|
||||
aiMsg.content += delta
|
||||
//每次更新messages消息,实现流式输出
|
||||
messages[messages.length - 1] = {
|
||||
...aiMsg
|
||||
}
|
||||
scrollToBottom()
|
||||
console.log('2')
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
//更新上下文消息
|
||||
done && historyUserMsgs.push(aiMsg)
|
||||
}
|
||||
}
|
||||
scrollToBottom()
|
||||
} catch (err) {
|
||||
aiMsg.content = '请重新发送'
|
||||
//更新messages消息
|
||||
messages[messages.length - 1] = {
|
||||
...aiMsg
|
||||
}
|
||||
console.error(err)
|
||||
} finally {
|
||||
loading.value = false
|
||||
showActions.value = false
|
||||
}
|
||||
}
|
185
src/pages/index/utils/index.ts
Normal file
@ -0,0 +1,185 @@
|
||||
export const fileSuffix = (str) => {
|
||||
if (!str) {
|
||||
return str
|
||||
}
|
||||
let reg = /\.\w*$/
|
||||
return str.match(reg)[0]
|
||||
}
|
||||
export const officeFileTypeList = ['.docx', '.doc', '.xls', '.xlsx', '.pdf', '.txt']
|
||||
export const videoFileType = ['.mp4', '.mov', '.wmv']
|
||||
export const picFileType = ['.jpg', '.png', '.jpeg']
|
||||
export const formatParams = (uploadList) => {
|
||||
// 上传文件formData类型
|
||||
|
||||
let photoList = [] // 媒体文件 参数中的content
|
||||
let videoList = []
|
||||
let fileList = [] // 文档文件
|
||||
uploadList.forEach((item) => {
|
||||
if (picFileType.includes(fileSuffix(item.ori_url))) {
|
||||
// 图片
|
||||
let media = {
|
||||
type: 'image_url',
|
||||
image_url: {
|
||||
url: item.ori_url,
|
||||
},
|
||||
}
|
||||
photoList.push(media)
|
||||
} else if (videoFileType.includes(fileSuffix(item.ori_url))) {
|
||||
// 视频
|
||||
let media = {
|
||||
type: 'video_url',
|
||||
video_url: {
|
||||
url: item.ori_url,
|
||||
poster: item.url,
|
||||
},
|
||||
}
|
||||
videoList.push(media)
|
||||
} else if (officeFileTypeList.includes(fileSuffix(item.ori_url))) {
|
||||
let file = {
|
||||
role: 'system',
|
||||
name: item.name,
|
||||
content: item.url,
|
||||
size: item.size,
|
||||
mask: 'new',
|
||||
}
|
||||
fileList.push(file)
|
||||
}
|
||||
})
|
||||
return { photoList: photoList, videoList: videoList, file: fileList }
|
||||
}
|
||||
export const calcFileSize = (size: number) => {
|
||||
const type = ['B', 'KB', 'MB', 'GB', 'TB']
|
||||
// for(let i=0;i<type.length && size>1024;i++){
|
||||
// size/=1024
|
||||
// }
|
||||
let unit = 'B'
|
||||
while (size > 1024) {
|
||||
size /= 1024
|
||||
unit = type.shift()
|
||||
}
|
||||
return `${Math.ceil(size)}${unit}`
|
||||
}
|
||||
// 格式化请求数据,从页面渲染的数据改为请求数据
|
||||
export function formatData(list) {
|
||||
let result = []
|
||||
let msg = null
|
||||
list.forEach((item, index) => {
|
||||
// if (index === list.length - 1) {
|
||||
// return
|
||||
// }
|
||||
|
||||
if (item.type === 'text') {
|
||||
result.push({
|
||||
role: item.role,
|
||||
content: item.content,
|
||||
type: item.type,
|
||||
mask: item.mask,
|
||||
})
|
||||
if (item.role !== 'assistant') {
|
||||
msg = {
|
||||
role: item.role,
|
||||
content: item.content,
|
||||
type: item.type,
|
||||
}
|
||||
}
|
||||
} else if (item.type === 'image' || item.type === 'video') {
|
||||
// 图片与视频混合在一起
|
||||
const content = []
|
||||
item.content.forEach((child) => {
|
||||
if (child.type === 'image_url') {
|
||||
content.push({
|
||||
type: 'image_url',
|
||||
image_url: {
|
||||
url: child.image_url.url,
|
||||
},
|
||||
})
|
||||
} else if (child.type === 'video_url') {
|
||||
content.push({
|
||||
type: 'video_url',
|
||||
video_url: {
|
||||
url: child.video_url.url,
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
if (msg) {
|
||||
content.push({
|
||||
type: 'text',
|
||||
text: msg.content,
|
||||
})
|
||||
msg = null
|
||||
}
|
||||
result.push({
|
||||
role: 'user',
|
||||
content: content,
|
||||
type: 'image',
|
||||
mask: item.mask,
|
||||
})
|
||||
} else if (item.type === 'file') {
|
||||
let content = []
|
||||
item.content.forEach((child) => {
|
||||
if (child.role === 'system') {
|
||||
content.push({
|
||||
role: 'system',
|
||||
content: child.content,
|
||||
type: 'file',
|
||||
mask: item.mask,
|
||||
})
|
||||
} else {
|
||||
console.log(child)
|
||||
}
|
||||
})
|
||||
if (msg) {
|
||||
content.push(msg)
|
||||
msg = null
|
||||
}
|
||||
result = result.concat(content)
|
||||
}
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
function sliceFile(blob: Blob, chunkSize: number): Blob[] {
|
||||
const chunks: Blob[] = []
|
||||
let cursor = 0
|
||||
while (cursor < blob.size) {
|
||||
chunks.push(blob.slice(cursor, cursor + chunkSize))
|
||||
cursor += chunkSize
|
||||
}
|
||||
return chunks
|
||||
}
|
||||
/**
|
||||
* 对 Base64 字符串进行切片
|
||||
*/
|
||||
export function sliceBase64(base64: string, chunkSize: number = 1024 * 1024): string[] {
|
||||
// 提取实际 Base64 数据部分
|
||||
// const base64Data = base64.split(',')[1]
|
||||
const byteStringLength = base64.length
|
||||
const slices = []
|
||||
console.log(base64)
|
||||
|
||||
for (let i = 0; i < byteStringLength; i += chunkSize) {
|
||||
const chunk = base64.slice(i, i + chunkSize)
|
||||
slices.push(chunk) // 可选:保留前缀
|
||||
}
|
||||
|
||||
return slices
|
||||
}
|
||||
|
||||
export async function readFile(file, chunkSize = 10 * 1024 * 1024) {
|
||||
const blob = await fetch(file.tempFilePath)
|
||||
const buffer = await blob.blob()
|
||||
return sliceFile(buffer, chunkSize)
|
||||
}
|
||||
|
||||
function uploadChunkFile({ chunk, fileName }, index, total, fileId) {
|
||||
const formData = new FormData()
|
||||
formData.append('Chunk', chunk)
|
||||
formData.append('ChunkFileName', `${fileName}_${index}`)
|
||||
formData.append('total', total)
|
||||
formData.append('UseType', 100)
|
||||
formData.append('FileName', fileName)
|
||||
formData.append('Source', 'aiChat')
|
||||
formData.append('UseType', 100)
|
||||
return
|
||||
}
|
101
src/pages/preview/index.vue
Normal file
@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<view class="flex flex-col h-90% bg-#ffffff">
|
||||
<view
|
||||
class="flex-none flex items-center justify-between px-5 py-3 bg-white shadow-md h-20 pt-10 z-999 fixed top-0 w-full box-border"
|
||||
>
|
||||
<image src="/static/aichat/black.png" class="w-3 h-4 mt-1" @click="goBack" />
|
||||
</view>
|
||||
<view class="image-gallery grid grid-cols-4 gap-1 p-4 mt-20" v-if="imageList.length > 0">
|
||||
<view
|
||||
v-for="(image, index) in imageList"
|
||||
:key="index"
|
||||
class="aspect-square overflow-hidden group"
|
||||
>
|
||||
<image
|
||||
:src="image.url"
|
||||
mode="aspectFill"
|
||||
class="w-full h-full"
|
||||
@click="handleImageClick(index)"
|
||||
/>
|
||||
|
||||
<!-- <wd-img :width="100" :height="100" :src="image.url" :enable-preview="true" /> -->
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 自定义预览弹窗 -->
|
||||
<view
|
||||
v-if="showPreview"
|
||||
class="fixed inset-0 bg-black bg-opacity-90 z-999 flex justify-center items-start pt-10"
|
||||
@click="closePreview"
|
||||
>
|
||||
<view class="relative w-full max-w-full mt-8 p-5 box-border">
|
||||
<image
|
||||
class="w-full max-h-[calc(100vh-90px)] object-contain"
|
||||
:src="previewUrl"
|
||||
mode="widthFix"
|
||||
@click.stop
|
||||
/>
|
||||
<view
|
||||
class="absolute top--8 right-5 text-white text-3xl z-1000 w-10 h-10 text-center leading-10"
|
||||
@click.stop="closePreview"
|
||||
>
|
||||
×
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="videoList.length > 0" class="flex items-center justify-center mt-30">
|
||||
<view v-for="(video, index) in videoList" :key="index" class="w-full h-50">
|
||||
<video :src="video" class="w-full h-full" controls :poster="video.url"></video>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
|
||||
// 图片列表数据
|
||||
const imageList = ref([])
|
||||
//视频列表
|
||||
const videoList = ref([])
|
||||
|
||||
// 图片点击处理
|
||||
// const handleImageClick = (index) => {
|
||||
// uni.previewImage({ urls: [imageList.value[index].url] })
|
||||
// }
|
||||
// 返回上一个页面
|
||||
const goBack = () => {
|
||||
uni.navigateBack({ delta: 1 })
|
||||
}
|
||||
|
||||
// 生命周期钩子
|
||||
onMounted(() => {
|
||||
//获取本地预览图片
|
||||
const previewImages = uni.getStorageSync('previewImages')
|
||||
//视频列表
|
||||
const previewVideos = uni.getStorageSync('previewVideos')
|
||||
|
||||
if (previewImages && previewImages.length > 0) {
|
||||
imageList.value = previewImages
|
||||
}
|
||||
|
||||
if (previewVideos && previewVideos.length > 0) {
|
||||
videoList.value = previewVideos
|
||||
}
|
||||
})
|
||||
|
||||
const showPreview = ref(false)
|
||||
const previewUrl = ref('')
|
||||
const previewTop = ref(30) // 距离顶部30px
|
||||
const handleImageClick = (index) => {
|
||||
previewUrl.value = imageList.value[index].url
|
||||
showPreview.value = true
|
||||
}
|
||||
|
||||
const closePreview = () => {
|
||||
showPreview.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
59
src/pages/webview/index.vue
Normal file
@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<web-view :src="link" :webview-styles="webviewStyles"></web-view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
link: '',
|
||||
webviewStyles: {
|
||||
width: '100%',
|
||||
progress: {
|
||||
color: '#999',
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
if (options && options.link) {
|
||||
this.link = options.link
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 只有 Webview 环境才有 plus
|
||||
// if (window.plus) {
|
||||
// const wv = plus.webview.currentWebview()
|
||||
// // 页面加载完后再注入 padding
|
||||
// wv.addEventListener('loaded', () => {
|
||||
// wv.evalJS(`
|
||||
// document.documentElement.style.padding = '30px';
|
||||
// document.body.style.margin = '0';
|
||||
// `)
|
||||
// })
|
||||
// }
|
||||
|
||||
uni.getSystemInfo({
|
||||
success: (res) => {
|
||||
// 获取状态栏高度和窗口高度
|
||||
const statusBarHeight = res.statusBarHeight
|
||||
const windowHeight = res.windowHeight
|
||||
|
||||
// 计算WebView高度(窗口高度减去状态栏高度)
|
||||
this.webviewStyles.height = windowHeight - statusBarHeight
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('获取系统信息失败:', err)
|
||||
// 默认使用窗口高度
|
||||
uni.getSystemInfo({
|
||||
success: (res) => {
|
||||
this.webviewStyles.height = res.windowHeight
|
||||
},
|
||||
})
|
||||
},
|
||||
})
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
@ -1,27 +1,158 @@
|
||||
import { http } from '@/utils/http'
|
||||
import { CustomRequestOptions } from '@/interceptors/request'
|
||||
import request from '@/utils/request'
|
||||
import * as API from '../app/types'
|
||||
|
||||
export interface IFooItem {
|
||||
id: string
|
||||
name: string
|
||||
}
|
||||
|
||||
/** GET 请求 */
|
||||
export const getFooAPI = (name: string) => {
|
||||
return http.get<IFooItem>('/foo', { name })
|
||||
export const getFooAPI = (path: string, name: string) => {
|
||||
return http.get<IFooItem>(path, { name })
|
||||
}
|
||||
/** GET 请求;支持 传递 header 的范例 */
|
||||
export const getFooAPI2 = (name: string) => {
|
||||
return http.get<IFooItem>('/foo', { name }, { 'Content-Type-100': '100' })
|
||||
export const getFooAPI2 = (path: string, name: string) => {
|
||||
return http.get<IFooItem>(path, { name }, { 'Content-Type-100': '100' })
|
||||
}
|
||||
|
||||
export interface IMessage {
|
||||
role: 'user' | 'system' | 'assistant'
|
||||
content: Array<{ type: 'text'; text: string } | { type: 'image_url'; image_url: string }>
|
||||
}
|
||||
|
||||
export interface IGptRequestBody {
|
||||
model: string
|
||||
max_tokens: number
|
||||
temperature: number
|
||||
top_p: number
|
||||
listUuid: string
|
||||
presence_penalty: number
|
||||
frequency_penalty: number
|
||||
messages: IMessage[]
|
||||
stream: boolean
|
||||
}
|
||||
|
||||
// GPT 响应类型(可根据你接口返回结构细化)
|
||||
export interface IGptResponse {
|
||||
id: string
|
||||
object: string
|
||||
choices: any[]
|
||||
}
|
||||
|
||||
// 👇 支持传 path 和 body 的 post 函数
|
||||
export const postGptAPI = (path: string, body: IGptRequestBody, headers?: Record<string, any>) => {
|
||||
return http.post<IGptResponse>(path, body)
|
||||
}
|
||||
// export const postUpload = (path: string, form: FormData, query?: Record<string, any>, headers?: Record<string, any>) => {
|
||||
// return http.post<IFooItem>(path, form,query,headers)
|
||||
// }
|
||||
|
||||
// 1. 定义一个新的扁平化接口
|
||||
export type UploadMultiForm = {
|
||||
/** 文件字段,key 为 k1…k10,可传单个 File 或 File[] */
|
||||
[K in `k${1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10}`]?: File | File[]
|
||||
} & {
|
||||
source: string // 画作来源
|
||||
mask: string // 画家 uid 或 用户 id
|
||||
type: string // 资源类型:image / file
|
||||
}
|
||||
|
||||
// 2. 改造 uploadMulti,让它直接把所有字段扁平化地 append 到 FormData
|
||||
export async function uploadMulti(form: UploadMultiForm, options?: CustomRequestOptions) {
|
||||
const formData = new FormData()
|
||||
|
||||
Object.entries(form).forEach(([key, val]) => {
|
||||
if (val instanceof File) {
|
||||
// 单个文件
|
||||
formData.append(key, val)
|
||||
} else if (Array.isArray(val)) {
|
||||
// 文件数组
|
||||
val.forEach((file) => formData.append(key, file))
|
||||
} else {
|
||||
// 普通文本字段
|
||||
formData.append(key, String(val))
|
||||
}
|
||||
})
|
||||
|
||||
return request<API.ApiResponse>('/upload/multi', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
// H5/浏览器 会自动带 boundary
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
data: formData,
|
||||
...(options || {}),
|
||||
})
|
||||
}
|
||||
|
||||
// export interface UploadMultiBody {
|
||||
// source: string // 画作来源
|
||||
// mask: string // 画家 uid 或 用户 id
|
||||
// detail: string // 详情描述
|
||||
// type: string // 资源类型:image / file
|
||||
// }
|
||||
|
||||
// export interface UploadMultiParams {
|
||||
// files?: File[] // 最多取前 3 个文件,分别对应 k1、k2、k3
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * 上传多文件到 /upload/multi
|
||||
// */
|
||||
// export async function uploadMulti({
|
||||
// params,
|
||||
// body,
|
||||
// options,
|
||||
// }: {
|
||||
// /** 文件和文本字段 */
|
||||
// params: UploadMultiParams
|
||||
// body: UploadMultiBody
|
||||
// /** uni.request / uni.uploadFile 的可选配置 */
|
||||
// options?: CustomRequestOptions
|
||||
// }) {
|
||||
// // 1. 构造 FormData
|
||||
// const formData = new FormData()
|
||||
// ;(params.files || []).slice(0, 10).forEach((file, idx) => {
|
||||
// formData.append(`k${idx + 1}`, file)
|
||||
// })
|
||||
// formData.append('source', body.source)
|
||||
// formData.append('mask', body.mask)
|
||||
// formData.append('detail', body.detail)
|
||||
// formData.append('type', body.type)
|
||||
|
||||
// // 2. 发起请求
|
||||
// return request<API.ApiResponse>('/upload/multi', {
|
||||
// method: 'POST',
|
||||
// headers: {
|
||||
// // 通常不需要手动写 Content-Type,浏览器/uniapp 会自动带 boundary
|
||||
// 'Content-Type': 'multipart/form-data',
|
||||
// },
|
||||
// data: formData,
|
||||
// ...(options || {}),
|
||||
// })
|
||||
// }
|
||||
|
||||
/** POST 请求 */
|
||||
export const postFooAPI = (name: string) => {
|
||||
return http.post<IFooItem>('/foo', { name })
|
||||
export const postFooAPI = (path: string, body: Record<string, any>) => {
|
||||
return http.post<IFooItem>(path, body)
|
||||
}
|
||||
/** POST 请求;需要传递 query 参数的范例;微信小程序经常有同时需要query参数和body参数的场景 */
|
||||
export const postFooAPI2 = (name: string) => {
|
||||
return http.post<IFooItem>('/foo', { name })
|
||||
export const postFooAPI2 = (
|
||||
path: string,
|
||||
body: Record<string, any>,
|
||||
query?: Record<string, any>,
|
||||
) => {
|
||||
return http.post<IFooItem>(path, body, query)
|
||||
}
|
||||
|
||||
/** POST 请求;支持 传递 header 的范例 */
|
||||
export const postFooAPI3 = (name: string) => {
|
||||
return http.post<IFooItem>('/foo', { name }, { name }, { 'Content-Type-100': '100' })
|
||||
export const postFooAPI3 = (
|
||||
path: string,
|
||||
body: Record<string, any>,
|
||||
query?: Record<string, any>,
|
||||
headers?: Record<string, any>,
|
||||
) => {
|
||||
return http.post<IFooItem>(path, body, query, headers)
|
||||
}
|
||||
|
BIN
src/static/aichat/Knowledge-close.png
Normal file
After Width: | Height: | Size: 776 B |
BIN
src/static/aichat/Knowledge-open.png
Normal file
After Width: | Height: | Size: 827 B |
BIN
src/static/aichat/add-circle.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
src/static/aichat/black.png
Normal file
After Width: | Height: | Size: 478 B |
BIN
src/static/aichat/close.png
Normal file
After Width: | Height: | Size: 360 B |
BIN
src/static/aichat/copy.png
Normal file
After Width: | Height: | Size: 455 B |
BIN
src/static/aichat/empty.png
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
src/static/aichat/enter-no.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
src/static/aichat/enter.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
src/static/aichat/files.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
src/static/aichat/ic_video.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
src/static/aichat/logo-message.png
Normal file
After Width: | Height: | Size: 5.3 KiB |
BIN
src/static/aichat/logo.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
src/static/aichat/new.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
src/static/aichat/phone-img.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
src/static/aichat/photo.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
src/static/aichat/resect.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
src/static/aichat/shirk-cl.png
Normal file
After Width: | Height: | Size: 361 B |
BIN
src/static/aichat/shrink.png
Normal file
After Width: | Height: | Size: 458 B |
BIN
src/static/aichat/stop.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
src/static/aichat/time.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
src/static/aichat/txt.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
src/static/aichat/word.png
Normal file
After Width: | Height: | Size: 5.3 KiB |
BIN
src/static/logo.png
Normal file
After Width: | Height: | Size: 10 KiB |
15
src/store/status/index.js
Normal file
@ -0,0 +1,15 @@
|
||||
import {ref} from 'vue'
|
||||
import {createGlobalState, useStorage} from "@vueuse/core";
|
||||
import {uniStorage} from "@/utils/uniStorage";
|
||||
export const useStatus =createGlobalState(()=>{
|
||||
const currentNavbar=ref({title:'',url:''})
|
||||
const applyTabbarIndex=ref(0)
|
||||
const statusBarHeight = ref(window?.plus?.navigator?.getStatusbarHeight() ?? 0)
|
||||
const tabBarIndex = useStorage('tabBarIndex', 0, uniStorage)
|
||||
return {
|
||||
statusBarHeight,
|
||||
applyTabbarIndex,
|
||||
currentNavbar,
|
||||
tabBarIndex
|
||||
}
|
||||
})
|
6
src/types/uni-pages.d.ts
vendored
@ -5,12 +5,14 @@
|
||||
|
||||
interface NavigateToOptions {
|
||||
url: "/pages/index/index" |
|
||||
"/pages/about/about";
|
||||
"/pages/about/about" |
|
||||
"/pages/preview/index" |
|
||||
"/pages/webview/index";
|
||||
}
|
||||
interface RedirectToOptions extends NavigateToOptions {}
|
||||
|
||||
interface SwitchTabOptions {
|
||||
url: "/pages/index/index" | "/pages/about/about"
|
||||
|
||||
}
|
||||
|
||||
type ReLaunchOptions = NavigateToOptions | SwitchTabOptions;
|
||||
|
9
src/utils/api.js
Normal file
@ -0,0 +1,9 @@
|
||||
import {httpPost,httpGet} from "./http"
|
||||
import {getEnvBaseUrl} from "./index"
|
||||
|
||||
const baseUrl=getEnvBaseUrl();
|
||||
|
||||
// 发送文本消息
|
||||
const endMsg=async (params)=>{
|
||||
return await httpGet(baseUrl+url,data)
|
||||
}
|
94
src/utils/guid.js
Normal file
@ -0,0 +1,94 @@
|
||||
function GUID() {
|
||||
this.date = new Date()
|
||||
|
||||
/* 判断是否初始化过,如果初始化过以下代码,则以下代码将不再执行,实际中只执行一次 */
|
||||
if (typeof this.newGUID != 'function') {
|
||||
/* 生成GUID码 */
|
||||
GUID.prototype.newGUID = function () {
|
||||
this.date = new Date()
|
||||
var guidStr = ''
|
||||
var sexadecimalDate = this.hexadecimal(this.getGUIDDate(), 16)
|
||||
var sexadecimalTime = this.hexadecimal(this.getGUIDTime(), 16)
|
||||
for (var i = 0; i < 9; i++) {
|
||||
guidStr += Math.floor(Math.random() * 16).toString(16)
|
||||
}
|
||||
guidStr += sexadecimalDate
|
||||
guidStr += sexadecimalTime
|
||||
while (guidStr.length < 32) {
|
||||
guidStr += Math.floor(Math.random() * 16).toString(16)
|
||||
}
|
||||
return this.formatGUID(guidStr)
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:获取当前日期的GUID格式,即8位数的日期:19700101
|
||||
* 返回值:返回GUID日期格式的字条串
|
||||
*/
|
||||
GUID.prototype.getGUIDDate = function () {
|
||||
return (
|
||||
this.date.getFullYear() +
|
||||
this.addZero(this.date.getMonth() + 1) +
|
||||
this.addZero(this.date.getDay())
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:获取当前时间的GUID格式,即8位数的时间,包括毫秒,毫秒为2位数:12300933
|
||||
* 返回值:返回GUID日期格式的字条串
|
||||
*/
|
||||
GUID.prototype.getGUIDTime = function () {
|
||||
return (
|
||||
this.addZero(this.date.getHours()) +
|
||||
this.addZero(this.date.getMinutes()) +
|
||||
this.addZero(this.date.getSeconds()) +
|
||||
this.addZero(parseInt(this.date.getMilliseconds() / 10))
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能: 为一位数的正整数前面添加0,如果是可以转成非NaN数字的字符串也可以实现
|
||||
* 参数: 参数表示准备再前面添加0的数字或可以转换成数字的字符串
|
||||
* 返回值: 如果符合条件,返回添加0后的字条串类型,否则返回自身的字符串
|
||||
*/
|
||||
GUID.prototype.addZero = function (num) {
|
||||
if (Number(num).toString() != 'NaN' && num >= 0 && num < 10) {
|
||||
return '0' + Math.floor(num)
|
||||
} else {
|
||||
return num.toString()
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:将y进制的数值,转换为x进制的数值
|
||||
* 参数:第1个参数表示欲转换的数值;第2个参数表示欲转换的进制;第3个参数可选,表示当前的进制数,如不写则为10
|
||||
* 返回值:返回转换后的字符串
|
||||
*/
|
||||
GUID.prototype.hexadecimal = function (num, x, y) {
|
||||
if (y != undefined) {
|
||||
return parseInt(num.toString(), y).toString(x)
|
||||
} else {
|
||||
return parseInt(num.toString()).toString(x)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 功能:格式化32位的字符串为GUID模式的字符串
|
||||
* 参数:第1个参数表示32位的字符串
|
||||
* 返回值:标准GUID格式的字符串
|
||||
*/
|
||||
GUID.prototype.formatGUID = function (guidStr) {
|
||||
var str1 = guidStr.slice(0, 8),
|
||||
str2 = guidStr.slice(8, 12),
|
||||
str3 = guidStr.slice(12, 16),
|
||||
str4 = guidStr.slice(16, 20),
|
||||
str5 = guidStr.slice(20)
|
||||
return str1 + str2 + str3 + str4 + str5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
getGuid: function () {
|
||||
return new GUID().newGUID()
|
||||
},
|
||||
}
|
281
src/utils/tools.js
Normal file
@ -0,0 +1,281 @@
|
||||
import { getEnvBaseUrl } from "./index";
|
||||
export const baseUrl =getEnvBaseUrl();
|
||||
export const api = (url = '', params = {}, method = 'post') => {
|
||||
// let authorization = uni.getStorageSync('authorization');
|
||||
// console.log(authorization);
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
url:baseUrl+url,
|
||||
data: params,
|
||||
method,
|
||||
header: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
//'authorization': authorization,
|
||||
},
|
||||
complete: (res) => {
|
||||
if (res.statusCode == 200) {
|
||||
resolve(res.data);
|
||||
} else {
|
||||
reject(res);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const api_form = (url = '', params = {}, method = 'post') => {
|
||||
let authorization = uni.getStorageSync('authorization');
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
url: baseUrl + url,
|
||||
data: params,
|
||||
method,
|
||||
header: {
|
||||
// 'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Content-Type': 'application/json',
|
||||
'authorization': authorization,
|
||||
},
|
||||
complete: (res) => {
|
||||
if (res.statusCode == 200) {
|
||||
resolve(res.data);
|
||||
} else {
|
||||
reject(res);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
// 接口获取
|
||||
export const apis = (url = '', params = {}, method = 'post') => {
|
||||
let authorization = uni.getStorageSync('authorization');
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
url: baseUrl + url,
|
||||
data: params,
|
||||
method,
|
||||
header: {
|
||||
// 'Content-Type':'multipart/form-data',//application/x-www-form-urlencoded',
|
||||
'authorization': authorization,
|
||||
},
|
||||
complete: (res) => {
|
||||
if (res.statusCode == 200) {
|
||||
resolve(res.data);
|
||||
} else {
|
||||
reject(res);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
// 错误toast提示
|
||||
export const showToastErr = (title) => {
|
||||
uni.showToast({
|
||||
title: title,
|
||||
icon: 'none',
|
||||
duration: 2000,
|
||||
mask: true
|
||||
});
|
||||
}
|
||||
// 成功toast提示
|
||||
export const showToastOk = (title) => {
|
||||
uni.showToast({
|
||||
title: title,
|
||||
icon: 'success',
|
||||
duration: 2000,
|
||||
mask: true
|
||||
});
|
||||
}
|
||||
export const showToastOkMask = (title) => {
|
||||
uni.showToast({
|
||||
title: title,
|
||||
icon: 'success',
|
||||
duration: 2000,
|
||||
mask: false
|
||||
});
|
||||
}
|
||||
export const showloading=(title)=>{
|
||||
uni.showLoading({
|
||||
mask:true,
|
||||
title,
|
||||
});
|
||||
}
|
||||
export const hideloading=()=>{
|
||||
uni.hideLoading();
|
||||
}
|
||||
export const navigateTo = (url) => {
|
||||
uni.navigateTo({
|
||||
url: url
|
||||
})
|
||||
}
|
||||
export const redirectTo = (url) => {
|
||||
uni.redirectTo({
|
||||
url: url
|
||||
})
|
||||
}
|
||||
export const reLaunch = (url) => {
|
||||
uni.reLaunch({
|
||||
url: url
|
||||
})
|
||||
}
|
||||
// 上传图片(选择图片)
|
||||
export const selectPic = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.chooseImage({
|
||||
count: 9, //默认9
|
||||
sizeType: ["original", "compressed"], //可以指定是原图还是压缩图,默认二者都有
|
||||
sourceType: ["album", "camera"], //从相册选择
|
||||
success: function(res) {
|
||||
resolve(res)
|
||||
},
|
||||
fail: function() {
|
||||
reject("选择文件失败")
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
// 上传图片
|
||||
export const uploadFile = (url,file) => {
|
||||
let authorization = uni.getStorageSync('authorization');
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.uploadFile({
|
||||
url: baseUrl + url,
|
||||
// filePath: file.tempFilePaths[0],
|
||||
name: 'file',
|
||||
header:{
|
||||
authorization: authorization
|
||||
},
|
||||
formData: {
|
||||
from: file,
|
||||
},
|
||||
complete: (res) => {
|
||||
if (res.statusCode == 200) {
|
||||
resolve(res)
|
||||
} else {
|
||||
reject(res)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
//滚动到元素位置
|
||||
export const smoothScroll = (element) => {
|
||||
setTimeout(() => {
|
||||
document.querySelector(element).scrollIntoView({
|
||||
behavior: "smooth",
|
||||
});
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
export function getCurrentTime() {
|
||||
var gettime = this
|
||||
const yy = new Date().getFullYear()
|
||||
const mm = new Date().getMonth() + 1
|
||||
const dd = new Date().getDate()
|
||||
const hh = new Date().getHours()
|
||||
const mf = new Date().getMinutes() < 10 ? '0' + new Date().getMinutes() : new Date().getMinutes()
|
||||
const ss = new Date().getSeconds() < 10 ? '0' + new Date().getSeconds() : new Date().getSeconds()
|
||||
gettime = yy + '-' + mm + '-' + dd + ' ' + hh + ':' + mf + ':' + ss
|
||||
|
||||
return gettime
|
||||
}
|
||||
export function getCurrentTime1() {
|
||||
var gettime = this
|
||||
const yy = new Date().getFullYear()
|
||||
const mm = new Date().getMonth() + 1
|
||||
const dd = new Date().getDate()
|
||||
const hh = new Date().getHours()
|
||||
const mf = new Date().getMinutes() < 10 ? '0' + new Date().getMinutes() : new Date().getMinutes()
|
||||
const ss = new Date().getSeconds() < 10 ? '0' + new Date().getSeconds() : new Date().getSeconds()
|
||||
gettime = yy + '-' + mm + '-' + dd
|
||||
|
||||
return gettime
|
||||
}
|
||||
|
||||
// 时间戳转日期(年月日)
|
||||
export const time_format = (time) => {
|
||||
// 判断时间戳是否为13位数,如果不是则*1000,时间戳只有13位数(带毫秒)和10(不带毫秒)位数的
|
||||
if (time.toString().length == 13) {
|
||||
var tme = new Date(time);
|
||||
} else {
|
||||
var tme = new Date(time * 1000);
|
||||
}
|
||||
var Y = tme.getFullYear();
|
||||
var M = (tme.getMonth() + 1 < 10 ? '0' + (tme.getMonth() + 1) : tme.getMonth() + 1);
|
||||
var D = tme.getDate();
|
||||
var h = tme.getHours();
|
||||
var m = tme.getMinutes();
|
||||
var s = tme.getSeconds();
|
||||
if(D<10){
|
||||
D='0'+D;
|
||||
}
|
||||
var tem1 = Y + '/' + M + '/' + D
|
||||
// + h + '时' + m + '分'
|
||||
// + s +'秒'
|
||||
return tem1;
|
||||
}
|
||||
export const time_format3 = (time,seq="-") =>{
|
||||
// 判断时间戳是否为13位数,如果不是则*1000,时间戳只有13位数(带毫秒)和10(不带毫秒)位数的
|
||||
if(time.toString().length == 13){
|
||||
var tme = new Date(time);
|
||||
}else{
|
||||
var tme = new Date(time * 1000);
|
||||
}
|
||||
var Y = tme.getFullYear();
|
||||
var M = (tme.getMonth() + 1 < 10 ? '0' + (tme.getMonth() + 1) : tme.getMonth() + 1);
|
||||
var D = tme.getDate();
|
||||
var h = tme.getHours();
|
||||
if(h<10){
|
||||
h='0'+h;
|
||||
}
|
||||
var m = tme.getMinutes();
|
||||
if(m<10){
|
||||
m='0'+m;
|
||||
}
|
||||
var s = tme.getSeconds();
|
||||
if(s<10){
|
||||
s='0'+s;
|
||||
}
|
||||
var tem1 = Y + seq + M + seq + D +' '+h+':'+m
|
||||
// + h + '时' + m + '分'
|
||||
// + s +'秒'
|
||||
return tem1;
|
||||
}
|
||||
// 时间戳转日期(时分)
|
||||
export const time_format1 = (time) =>{
|
||||
if(time.toString().length == 13){
|
||||
var tme = new Date(time);
|
||||
}else{
|
||||
var tme = new Date(time * 1000);
|
||||
}
|
||||
var Y = tme.getFullYear();
|
||||
var M = (tme.getMonth() + 1 < 10 ? '0' + (tme.getMonth() + 1) : tme.getMonth() + 1);
|
||||
var D = tme.getDate();
|
||||
var h = tme.getHours();
|
||||
var m = tme.getMinutes();
|
||||
var s = tme.getSeconds();
|
||||
var tem2 = + h + '时' + m + '分'
|
||||
// + s +'秒'
|
||||
return tem2;
|
||||
}
|
||||
// 时间戳转日期(时分 00:00格式)
|
||||
export const toHHmmss= (data)=> {
|
||||
var time;
|
||||
var hours = parseInt((data % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
|
||||
var minutes = parseInt((data % (1000 * 60 * 60)) / (1000 * 60));
|
||||
// var seconds = (data % (1000 * 60)) / 1000;
|
||||
time = (hours < 10 ? ('0' + hours) : hours) + ':' + (minutes < 10 ? ('0' + minutes) : minutes) ;
|
||||
return time;
|
||||
}
|
||||
// 日期转时间戳(10)
|
||||
export const date_stamp = (time) => new Date(time).getTime() / 1000
|
||||
// 日期转时间戳(13)
|
||||
export const date_stamp1 = (time) => new Date(time).getTime()
|
||||
// 树节点
|
||||
export function travelTree(tree, arr) {
|
||||
for (let item of tree) {
|
||||
arr.push(item.label);
|
||||
if (item.children && item.children.length) travelTree(item.children, arr);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
11
src/utils/uniStorage.js
Normal file
@ -0,0 +1,11 @@
|
||||
export const uniStorage = {
|
||||
getItem(key) {
|
||||
return uni.getStorageSync(key) || null
|
||||
},
|
||||
setItem(key, value) {
|
||||
return uni.setStorageSync(key, value)
|
||||
},
|
||||
removeItem(key) {
|
||||
return uni.removeStorageSync(key)
|
||||
},
|
||||
}
|
3
src/utils/uploadFile.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export const uploadFile = (url: string, options) => {
|
||||
console.log('options: ', options)
|
||||
}
|
BIN
static/app/icons/1024x1024.png
Normal file
After Width: | Height: | Size: 58 KiB |
BIN
static/app/icons/120x120.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
static/app/icons/144x144.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
static/app/icons/152x152.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
static/app/icons/167x167.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
static/app/icons/180x180.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
static/app/icons/192x192.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
static/app/icons/20x20.png
Normal file
After Width: | Height: | Size: 574 B |
BIN
static/app/icons/29x29.png
Normal file
After Width: | Height: | Size: 780 B |
BIN
static/app/icons/40x40.png
Normal file
After Width: | Height: | Size: 985 B |
BIN
static/app/icons/58x58.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
static/app/icons/60x60.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
static/app/icons/72x72.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
static/app/icons/76x76.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
static/app/icons/80x80.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
static/app/icons/87x87.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
static/app/icons/96x96.png
Normal file
After Width: | Height: | Size: 2.3 KiB |