Compare commits

..

8 Commits

Author SHA1 Message Date
75475b3854 Merge pull request 'main' (#98) from main into prod
Reviewed-on: #98
2026-04-27 11:07:18 +08:00
ping
c4e45597a4 update loginUser 2026-04-27 11:00:17 +08:00
ping
19ba9aac26 update logintype 2026-04-27 10:30:33 +08:00
hrx
22204bedec updata 2026-04-27 10:27:48 +08:00
hrx
f31a47f56c updata 2026-04-27 10:16:16 +08:00
hrx
89fed5a6ea updata 2026-04-27 10:07:38 +08:00
hrx
feee90ffbd updata 2026-04-27 09:41:30 +08:00
hrx
c84cf055ef updata 2026-04-24 11:22:01 +08:00
4 changed files with 520 additions and 550 deletions

View File

@ -153,6 +153,10 @@ async def loginUser(ns):
if type1 == 1:
# 手机号验证码登录
userreacs = await sor.R('users', {'mobile': ns.get('username')})
if not userreacs:
userreacs = await sor.R('users', {'username': ns.get('username')})
# 如果是微信扫码后绑定已有账号
if ns.get('wechat_openid'):
if userreacs:

View File

@ -43,57 +43,101 @@ async def logintype(ns):
db = DBPools()
async with db.sqlorContext('kboss') as sor:
type = 0
if ns.get('codeid'):
type += 1
domain_name = ns.get('domain_name')
if domain_name in ['www.opencomputing.cn', 'dev.opencomputing.cn', 'localhost:9527'] and ns.get('username') != '开元云(北京)科技有限公司':
if not ns.get('mobile'):
return {
'status': False,
'msg': '请输入手机号'
}
real_mobile_li = await sor.R('users', {'username': ns['username']})
if not real_mobile_li:
return {
'status': False,
'msg': '用户名有误'
}
real_mobile = real_mobile_li[0]['mobile'] if real_mobile_li else None
if not real_mobile:
return {
'status': False,
'msg': '未查询到匹配得手机号码'
}
if real_mobile and ns['mobile'] != real_mobile:
return {
'status': False,
'msg': '您的手机号与用户名不匹配'
}
if not ns.get('codeid'):
return {
'status': False,
'msg': 'codeid不存在, 请输入验证码'
}
code = await sor.R('validatecode', {'id': ns.get('codeid'), 'vcode': ns.get('vcode')})
if len(code) < 1:
return {'status': False, 'msg': '验证码不正确'}
if type == 1:
# 手机号登录
users = await sor.R('users', {'mobile': ns.get('username')})
else:
password = password_encode(ns['password'])
users = await sor.R('users', {'username': ns.get('username'), 'password': password})
if len(users) < 1:
return {"status": False,'msg':'用户名或密码错误'}
elif ns.get('username') == "admin":
if len(users) < 1:
return {"status": False,'msg':'用户名或密码错误'}
return {'status': True}
else:
reseller = await sor.R('reseller', {'domain_name': ns.get('domain_name')})
# 查到用户的所在机构
user_org = await sor.R('organization', {'id': users[0]['orgid']})
#0代表用户为主 1代表用户为子
user_type = 0
# 子页面判断用户
if len(reseller) >= 1:
if reseller[0]['orgid'] == user_org[0]['parentid']:
user_type += 1
if reseller[0]['orgid'] == users[0]['orgid']:
user_type += 1
# 主页面判断用户
type = 0
if ns.get('codeid'):
type += 1
code = await sor.R('validatecode', {'id': ns.get('codeid'), 'vcode': ns.get('vcode')})
if len(code) < 1:
return {'status': False, 'msg': '验证码不正确'}
if type == 1:
# 手机号登录
users = await sor.R('users', {'mobile': ns.get('username')})
else:
resellers = await sor.R('reseller', {'orgid': user_org[0]['parentid']})
if len(resellers) >= 1:
user_type += 1
if users[0]['user_reseller'] == '1':
user_type += 1
# 证明是主级页面
if len(reseller) < 1:
# 用户为主
if user_type == 0:
return {'status': True}
password = password_encode(ns['password'])
users = await sor.R('users', {'username': ns.get('username'), 'password': password})
if len(users) < 1:
return {"status": False,'msg':'用户名或密码错误'}
elif ns.get('username') == "admin":
return {'status': True}
else:
reseller = await sor.R('reseller', {'domain_name': ns.get('domain_name')})
# 查到用户的所在机构
user_org = await sor.R('organization', {'id': users[0]['orgid']})
#0代表用户为主 1代表用户为子
user_type = 0
# 子页面判断用户
if len(reseller) >= 1:
if reseller[0]['orgid'] == user_org[0]['parentid']:
user_type += 1
if reseller[0]['orgid'] == users[0]['orgid']:
user_type += 1
# 主页面判断用户
else:
resellers = await sor.R('reseller', {'orgid': user_org[0]['parentid']})
if len(resellers) >= 1:
user_type += 1
if users[0]['user_reseller'] == '1':
user_type += 1
# 证明是主级页面
if len(reseller) < 1:
# 用户为主
if user_type == 0:
return {'status': True}
else:
# 用户为子
return {'status': False,'msg':'用户名或密码错误'}
# 子级页面
else:
# 用户为主
if user_type == 0:
return {'status': False,'msg':'用户名或密码错误'}
# 用户为子
return {'status': False,'msg':'用户名或密码错误'}
# 子级页面
else:
# 用户为主
if user_type == 0:
return {'status': False,'msg':'用户名或密码错误'}
# 用户为子
else:
return {'status': True}
else:
return {'status': True}
ret = await logintype(params_kw)
return ret

View File

@ -160,8 +160,13 @@
</div>
</div>
<div v-show="aiDialogVisible" class="ai-chat-panel">
<div class="ai-chat-panel__header">
<div
v-show="aiDialogVisible"
ref="aiChatPanel"
class="ai-chat-panel"
:style="aiPanelStyle"
>
<div class="ai-chat-panel__header" @mousedown="startAIPanelDrag">
<div class="ai-chat-panel__title">
<span class="ai-chat-panel__logo">AI</span>
<span>有问题找开元</span>
@ -260,6 +265,15 @@ export default Vue.extend({
aiInput: '',
aiLoading: false,
aiMessages: [],
aiPanelPosition: {
left: null,
top: null
},
aiPanelDragging: false,
aiPanelDragOffset: {
x: 0,
y: 0
},
aiQuickQuestions: [
'推荐适合训练大模型的 GPU 服务器',
'4090 和 A100 怎么选',
@ -308,6 +322,9 @@ export default Vue.extend({
}
});
},
beforeDestroy() {
this.stopAIPanelDrag()
},
computed: {
...mapGetters(["sidebar", "avatar", "device"]),
...mapState({
@ -342,6 +359,18 @@ export default Vue.extend({
} else {
return this.$route.path.includes('/homePage/index');
}
},
aiPanelStyle() {
if (this.aiPanelPosition.left === null || this.aiPanelPosition.top === null) {
return {}
}
return {
left: `${this.aiPanelPosition.left}px`,
top: `${this.aiPanelPosition.top}px`,
right: 'auto',
bottom: 'auto'
}
}
},
methods: {
@ -350,12 +379,14 @@ export default Vue.extend({
handleAIClick() {
this.aiDialogVisible = true
this.$nextTick(() => {
this.initAIPanelPosition()
this.scrollAIChatToBottom()
})
},
closeAIPanel() {
this.aiDialogVisible = false
this.stopAIPanelDrag()
},
resetAIChat() {
@ -367,6 +398,73 @@ export default Vue.extend({
this.sendAIMessage(question)
},
initAIPanelPosition() {
if (this.aiPanelPosition.left !== null && this.aiPanelPosition.top !== null) {
return
}
const panel = this.$refs.aiChatPanel
if (!panel) return
const panelWidth = panel.offsetWidth || 380
const panelHeight = panel.offsetHeight || 620
this.aiPanelPosition = {
left: Math.max(window.innerWidth - panelWidth - 24, 16),
top: Math.max(window.innerHeight - panelHeight - 24, 16)
}
},
startAIPanelDrag(event) {
if (event.button !== 0) return
if (event.target && event.target.closest('.ai-chat-panel__close')) {
return
}
const panel = this.$refs.aiChatPanel
if (!panel) return
const rect = panel.getBoundingClientRect()
this.aiPanelDragging = true
this.aiPanelDragOffset = {
x: event.clientX - rect.left,
y: event.clientY - rect.top
}
window.addEventListener('mousemove', this.handleAIPanelDrag)
window.addEventListener('mouseup', this.stopAIPanelDrag)
document.body.style.userSelect = 'none'
},
handleAIPanelDrag(event) {
if (!this.aiPanelDragging) return
const panel = this.$refs.aiChatPanel
if (!panel) return
const panelWidth = panel.offsetWidth || 380
const panelHeight = panel.offsetHeight || 620
const minLeft = 0
const minTop = 0
const maxLeft = Math.max(window.innerWidth - panelWidth, 0)
const maxTop = Math.max(window.innerHeight - panelHeight, 0)
const nextLeft = event.clientX - this.aiPanelDragOffset.x
const nextTop = event.clientY - this.aiPanelDragOffset.y
this.aiPanelPosition = {
left: Math.min(Math.max(nextLeft, minLeft), maxLeft),
top: Math.min(Math.max(nextTop, minTop), maxTop)
}
},
stopAIPanelDrag() {
this.aiPanelDragging = false
window.removeEventListener('mousemove', this.handleAIPanelDrag)
window.removeEventListener('mouseup', this.stopAIPanelDrag)
document.body.style.userSelect = ''
},
handleAIKeydown(event) {
if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault()
@ -1251,6 +1349,7 @@ export default Vue.extend({
justify-content: space-between;
border-bottom: 1px solid #f0f0f0;
background: #fff;
cursor: move;
}
&__title {

File diff suppressed because it is too large Load Diff