merged v4.0.9 and fixed conflicts

This commit is contained in:
RockYang
2024-09-05 11:02:32 +08:00
47 changed files with 1927 additions and 427 deletions

View File

@@ -221,7 +221,7 @@
</template>
<script setup>
import {ref, watch} from "vue"
import {nextTick, onUnmounted, ref, watch} from "vue"
import {httpGet, httpPost} from "@/utils/http";
import {ElMessage} from "element-plus";
import {setUserToken} from "@/store/session";
@@ -339,7 +339,8 @@ const submitRegister = () => {
}
const close = function () {
emits('hide', false);
emits('hide', false)
login.value = true
}
</script>

View File

@@ -6,19 +6,20 @@
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
import {createRouter, createWebHistory} from "vue-router";
import {ref} from "vue";
import {httpGet} from "@/utils/http";
const routes = [
{
name: 'Index',
path: '/',
meta: {title: process.env.VUE_APP_TITLE},
meta: {title: "首页"},
component: () => import('@/views/Index.vue'),
},
{
name: 'home',
path: '/home',
redirect: '/chat',
meta: {title: '首页'},
component: () => import('@/views/Home.vue'),
children: [
{
@@ -273,11 +274,22 @@ const router = createRouter({
routes: routes,
})
const active = ref(false)
const title = ref('')
httpGet("/api/config/license").then(res => {
active.value = res.data.de_copy
}).catch(() => {})
httpGet("/api/config/get?key=system").then(res => {
title.value = res.data.title
}).catch(()=>{})
let prevRoute = null
// dynamic change the title when router change
router.beforeEach((to, from, next) => {
if (to.meta.title) {
if (!active.value) {
document.title = `${to.meta.title} | ${process.env.VUE_APP_TITLE}`
} else {
document.title = `${to.meta.title} | ${title.value}`
}
prevRoute = from
next()

View File

@@ -222,15 +222,29 @@ export function processContent(content) {
return texts.join("\n")
}
export function escapeHTML(html) {
return html.replace(/&/g, "&amp;")
export function processPrompt(prompt) {
prompt = prompt.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;");
const linkRegex = /(https?:\/\/\S+)/g;
const links = prompt.match(linkRegex);
if (links) {
for (let link of links) {
if (isImage(link)) {
const index = prompt.indexOf(link)
if (prompt.substring(index - 1, 2) !== "]") {
prompt = prompt.replace(link, "\n![](" + link + ")\n")
}
}
}
}
return prompt
}
// 判断是否为 iphone 设备
export function isIphone() {
return /iPhone/i.test(navigator.userAgent) && !/iPad/i.test(navigator.userAgent);
// 判断是否为微信浏览器
export function isWeChatBrowser() {
return /MicroMessenger/i.test( navigator.userAgent);
}
export function showLoginDialog(router) {

View File

@@ -191,7 +191,15 @@ import ChatPrompt from "@/components/ChatPrompt.vue";
import ChatReply from "@/components/ChatReply.vue";
import {Delete, Edit, More, Plus, Promotion, Search, Share, VideoPause} from '@element-plus/icons-vue'
import 'highlight.js/styles/a11y-dark.css'
import {dateFormat, escapeHTML, isMobile, processContent, randString, removeArrayItem, UUID} from "@/utils/libs";
import {
dateFormat,
isMobile,
processContent,
processPrompt,
randString,
removeArrayItem,
UUID
} from "@/utils/libs";
import {ElMessage, ElMessageBox} from "element-plus";
import hl from "highlight.js";
import {getSessionId, getUserToken, removeUserToken} from "@/store/session";
@@ -697,8 +705,10 @@ const onInput = (e) => {
inputRef.value.scrollTo(0, inputRef.value.scrollHeight)
if (prompt.value.length < 10) {
row.value = 1
} else if (row.value <= 7) {
} else if (lines <= 7){
row.value = lines
} else {
row.value = 7
}
// 输入回车自动提交
@@ -738,7 +748,7 @@ const sendMessage = function () {
type: "prompt",
id: randString(32),
icon: loginUser.value.avatar,
content: md.render(escapeHTML(processContent(prompt.value))),
content: md.render(processPrompt(prompt.value)),
created_at: new Date().getTime() / 1000,
});

View File

@@ -167,11 +167,6 @@
<i class="iconfont icon-loading"></i>
<span>正在下载图片</span>
</div>
<div class="image-slot">
<el-icon>
<Picture/>
</el-icon>
</div>
</template>
</el-image>

View File

@@ -46,19 +46,21 @@
<span class="username">{{ loginUser.nickname }}</span>
</el-dropdown-item>
<el-dropdown-item>
<i class="iconfont icon-book"></i>
<a href="https://github.com/yangjian102621/chatgpt-plus" target="_blank">
用户手册
</a>
</el-dropdown-item>
<div v-if="!licenseConfig.de_copy">
<el-dropdown-item>
<i class="iconfont icon-book"></i>
<a href="https://github.com/yangjian102621/chatgpt-plus" target="_blank">
用户手册
</a>
</el-dropdown-item>
<el-dropdown-item>
<i class="iconfont icon-github"></i>
<a href="https://ai.r9it.com/docs/" target="_blank">
Geek-AI {{ version }}
</a>
</el-dropdown-item>
<el-dropdown-item>
<i class="iconfont icon-github"></i>
<a href="https://ai.r9it.com/docs/" target="_blank">
Geek-AI {{ version }}
</a>
</el-dropdown-item>
</div>
<el-divider style="margin: 2px 0"/>
<el-dropdown-item @click="logout">
<i class="iconfont icon-logout"></i>
@@ -185,7 +187,7 @@ onMounted(() => {
httpGet("/api/menu/list").then(res => {
mainNavs.value = res.data
// 根据窗口的高度计算应该显示多少菜单
const rows = Math.floor((window.innerHeight - 90) / 60)
const rows = Math.floor((window.innerHeight - 100) / 90)
if (res.data.length > rows) {
mainNavs.value = res.data.slice(0, rows)
moreNavs.value = res.data.slice(rows)

View File

@@ -515,6 +515,13 @@
正在加载图片
</div>
</template>
<template #error>
<div class="image-slot">
<el-icon>
<Picture/>
</el-icon>
</div>
</template>
</el-image>
<el-image v-else>
<template #error>
@@ -522,11 +529,6 @@
<i class="iconfont icon-loading"></i>
<span>正在下载图片</span>
</div>
<div class="image-slot">
<el-icon>
<Picture/>
</el-icon>
</div>
</template>
</el-image>

View File

@@ -308,11 +308,7 @@
</template>
<template #error>
<div class="image-slot">
<el-icon>
<Picture/>
</el-icon>
</div>
<div class="image-slot"></div>
</template>
</el-image>

View File

@@ -26,8 +26,11 @@
</el-button>
</a>
</span>
<el-button @click="router.push('/login')" round>登录</el-button>
<el-button @click="router.push('/register')" round>注册</el-button>
<span v-if="!isLogin">
<el-button @click="router.push('/login')" round>登录</el-button>
<el-button @click="router.push('/register')" round>注册</el-button>
</span>
</div>
</el-menu>
</div>
@@ -69,6 +72,7 @@ import FooterBar from "@/components/FooterBar.vue";
import {httpGet} from "@/utils/http";
import {ElMessage} from "element-plus";
import {isMobile} from "@/utils/libs";
import {checkSession} from "@/action/session";
const router = useRouter()
@@ -83,6 +87,7 @@ const licenseConfig = ref({})
// const size = Math.max(window.innerWidth * 0.5, window.innerHeight * 0.8)
const winHeight = window.innerHeight - 150
const bgClass = ref('fixed-bg')
const isLogin = ref(false)
onMounted(() => {
httpGet("/api/config/get?key=system").then(res => {
@@ -91,6 +96,9 @@ onMounted(() => {
if (res.data.rand_bg) {
bgClass.value = "rand-bg"
}
if (res.data.slogan) {
slogan.value = res.data.slogan
}
}).catch(e => {
ElMessage.error("获取系统配置失败:" + e.message)
})
@@ -100,11 +108,11 @@ onMounted(() => {
}).catch(e => {
ElMessage.error("获取 License 配置:" + e.message)
})
init()
})
const init = () => {
}
checkSession().then(() => {
isLogin.value = true
}).catch(()=>{})
})
</script>
<style lang="stylus" scoped>

View File

@@ -5,9 +5,8 @@
<h2>会员推广计划</h2>
<div class="share-box">
<div class="info">
我们非常欢迎您把此应用分享给您身边的朋友分享成功注册后您将获得 <strong>{{ inviteChatCalls }}</strong>
次对话额度以及
<strong>{{ inviteImgCalls }}</strong> 次AI绘画额度作为奖励
我们非常欢迎您把此应用分享给您身边的朋友分享成功注册后您和被邀请人都将获得 <strong>{{ invitePower }}</strong>
算力额度作为奖励
你可以保存下面的二维码或者直接复制分享您的专属推广链接发送给微信好友
</div>
@@ -99,8 +98,7 @@ import {useSharedStore} from "@/store/sharedata";
const inviteURL = ref("")
const qrImg = ref("/images/wx.png")
const inviteChatCalls = ref(0)
const inviteImgCalls = ref(0)
const invitePower = ref(0)
const hits = ref(0)
const regNum = ref(0)
const rate = ref(0)
@@ -144,8 +142,7 @@ const initData = () => {
})
httpGet("/api/config/get?key=system").then(res => {
inviteChatCalls.value = res.data["invite_chat_calls"]
inviteImgCalls.value = res.data["invite_img_calls"]
invitePower.value = res.data["invite_power"]
}).catch(e => {
ElMessage.error("获取系统配置失败:" + e.message)
})

View File

@@ -76,6 +76,9 @@
<el-button type="success" @click="PayJs(scope.item)" size="small" v-if="payWays['payjs']">
<span><i class="iconfont icon-wechat-pay"></i> 微信</span>
</el-button>
<el-button type="success" @click="wechatPay(scope.item)" size="small" v-if="payWays['wechat']">
<i class="iconfont icon-wechat-pay"></i> 微信
</el-button>
</div>
</div>
</div>
@@ -237,9 +240,11 @@ onMounted(() => {
// refresh payment qrcode
const refreshPayCode = () => {
if (curPay.value === 'alipay') {
alipay()
alipay(curPayProduct.value)
} else if (curPay.value === 'hupi') {
huPiPay()
huPiPay(curPayProduct.value)
} else if (curPay.value === 'payjs') {
PayJs(curPayProduct.value)
}
}
@@ -312,6 +317,21 @@ const PayJs = (row) => {
genPayQrcode()
}
const wechatPay = (row) => {
payName.value = '微信'
curPay.value = "wechat"
amount.value = (row.price - row.discount).toFixed(2)
if (!isLogin.value) {
store.setShowLoginDialog(true)
return
}
if (row) {
curPayProduct.value = row
}
genPayQrcode()
}
const queryOrder = (orderNo) => {
httpPost("/api/payment/query", {order_no: orderNo}).then(res => {
if (res.data.status === 1) {

View File

@@ -13,6 +13,9 @@
<el-form-item label="控制台标题" prop="admin_title">
<el-input v-model="system['admin_title']"/>
</el-form-item>
<el-form-item label="网站Slogan" prop="slogan">
<el-input v-model="system['slogan']"/>
</el-form-item>
<el-form-item label="网站 LOGO" prop="logo">
<el-input v-model="system['logo']" placeholder="网站LOGO图片">
<template #append>
@@ -44,7 +47,7 @@
</el-tooltip>
</el-form-item>
<el-form-item label="随机背景">
<el-form-item label="动态背景">
<el-switch v-model="system['rand_bg']"/>
<el-tooltip
effect="dark"

View File

@@ -1,9 +1,7 @@
<template>
<div class="index container">
<h2 class="title">{{title}}</h2>
<van-notice-bar left-icon="info-o" :scrollable="true">
你有多少想象力AI就有多大创造力我辈之人先干为敬陪您先把 AI 用起来
</van-notice-bar>
<van-notice-bar left-icon="info-o" :scrollable="true">{{slogan}}}</van-notice-bar>
<div class="content">
<van-grid :column-num="3" :gutter="10" border>
@@ -90,8 +88,18 @@ const isLogin = ref(false)
const apps = ref([])
const loading = ref(false)
const roles = ref([])
const slogan = ref('你有多大想象力AI就有多大创造力')
onMounted(() => {
httpGet("/api/config/get?key=system").then(res => {
title.value = res.data.title
if (res.data.slogan) {
slogan.value = res.data.slogan
}
}).catch(e => {
ElMessage.error("获取系统配置失败:" + e.message)
})
checkSession().then((user) => {
isLogin.value = true
roles.value = user.chat_roles

View File

@@ -69,6 +69,9 @@
<van-button type="success" @click="pay('payjs',item)" size="small" v-if="payWays['payjs']">
<span><i class="iconfont icon-wechat-pay"></i> 微信</span>
</van-button>
<van-button type="primary" @click="pay('wechat',item)" size="small" v-if="payWays['wechat']">
<i class="iconfont icon-wechat-pay"></i> 微信
</van-button>
</div>
</h4>
@@ -151,10 +154,10 @@
<script setup>
import {onMounted, ref} from "vue";
import {showFailToast, showNotify, showSuccessToast} from "vant";
import {showFailToast, showNotify, showSuccessToast, showToast} from "vant";
import {httpGet, httpPost} from "@/utils/http";
import Compressor from 'compressorjs';
import {dateFormat, showLoginDialog} from "@/utils/libs";
import {dateFormat, isWeChatBrowser, showLoginDialog} from "@/utils/libs";
import {ElMessage} from "element-plus";
import {checkSession} from "@/action/session";
import {useRouter} from "vue-router";
@@ -296,8 +299,11 @@ const pay = (payWay, item) => {
product_id: item.id,
user_id: userId.value
}).then(res => {
// console.log(res.data)
location.href = res.data
if (isWeChatBrowser() && payWay === 'wechat') {
showFailToast("请在系统自带浏览器打开支付页面,或者在 PC 端进行扫码支付")
} else {
location.href = res.data
}
}).catch(e => {
showFailToast("生成支付订单失败:" + e.message)
})