mirror of
				https://github.com/yangjian102621/geekai.git
				synced 2025-11-04 16:23:42 +08:00 
			
		
		
		
	feat: optimize login dialog
This commit is contained in:
		@@ -146,6 +146,7 @@ func authorizeMiddleware(s *AppServer, client *redis.Client) gin.HandlerFunc {
 | 
			
		||||
			c.Request.URL.Path == "/api/invite/hits" ||
 | 
			
		||||
			c.Request.URL.Path == "/api/sd/imgWall" ||
 | 
			
		||||
			c.Request.URL.Path == "/api/sd/client" ||
 | 
			
		||||
			c.Request.URL.Path == "/api/config/get" ||
 | 
			
		||||
			strings.HasPrefix(c.Request.URL.Path, "/api/test") ||
 | 
			
		||||
			strings.HasPrefix(c.Request.URL.Path, "/api/function/") ||
 | 
			
		||||
			strings.HasPrefix(c.Request.URL.Path, "/api/sms/") ||
 | 
			
		||||
 
 | 
			
		||||
@@ -277,6 +277,128 @@ func (h *PaymentHandler) PayQrcode(c *gin.Context) {
 | 
			
		||||
	resp.SUCCESS(c, gin.H{"order_no": orderNo, "image": fmt.Sprintf("data:image/jpg;base64, %s", imgDataBase64), "url": imageURL})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Mobile 移动端支付
 | 
			
		||||
func (h *PaymentHandler) Mobile(c *gin.Context) {
 | 
			
		||||
 | 
			
		||||
	var data struct {
 | 
			
		||||
		PayWay    string `json:"pay_way"` // 支付方式
 | 
			
		||||
		ProductId uint   `json:"product_id"`
 | 
			
		||||
		UserId    int    `json:"user_id"`
 | 
			
		||||
	}
 | 
			
		||||
	if err := c.ShouldBindJSON(&data); err != nil {
 | 
			
		||||
		resp.ERROR(c, types.InvalidArgs)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var product model.Product
 | 
			
		||||
	res := h.db.First(&product, data.ProductId)
 | 
			
		||||
	if res.Error != nil {
 | 
			
		||||
		resp.ERROR(c, "Product not found")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	orderNo, err := h.snowflake.Next(false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		resp.ERROR(c, "error with generate trade no: "+err.Error())
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	var user model.User
 | 
			
		||||
	res = h.db.First(&user, data.UserId)
 | 
			
		||||
	if res.Error != nil {
 | 
			
		||||
		resp.ERROR(c, "Invalid user ID")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var payWay string
 | 
			
		||||
	var notifyURL string
 | 
			
		||||
	switch data.PayWay {
 | 
			
		||||
	case "hupi":
 | 
			
		||||
		payWay = PayWayXunHu
 | 
			
		||||
		notifyURL = h.App.Config.HuPiPayConfig.NotifyURL
 | 
			
		||||
	case "payjs":
 | 
			
		||||
		payWay = PayWayJs
 | 
			
		||||
		notifyURL = h.App.Config.JPayConfig.NotifyURL
 | 
			
		||||
	default:
 | 
			
		||||
		payWay = PayWayAlipay
 | 
			
		||||
		notifyURL = h.App.Config.AlipayConfig.NotifyURL
 | 
			
		||||
	}
 | 
			
		||||
	// 创建订单
 | 
			
		||||
	remark := types.OrderRemark{
 | 
			
		||||
		Days:     product.Days,
 | 
			
		||||
		Power:    product.Power,
 | 
			
		||||
		Name:     product.Name,
 | 
			
		||||
		Price:    product.Price,
 | 
			
		||||
		Discount: product.Discount,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	amount, _ := decimal.NewFromFloat(product.Price).Sub(decimal.NewFromFloat(product.Discount)).Float64()
 | 
			
		||||
	order := model.Order{
 | 
			
		||||
		UserId:    user.Id,
 | 
			
		||||
		Username:  user.Username,
 | 
			
		||||
		ProductId: product.Id,
 | 
			
		||||
		OrderNo:   orderNo,
 | 
			
		||||
		Subject:   product.Name,
 | 
			
		||||
		Amount:    amount,
 | 
			
		||||
		Status:    types.OrderNotPaid,
 | 
			
		||||
		PayWay:    payWay,
 | 
			
		||||
		Remark:    utils.JsonEncode(remark),
 | 
			
		||||
	}
 | 
			
		||||
	res = h.db.Create(&order)
 | 
			
		||||
	if res.Error != nil || res.RowsAffected == 0 {
 | 
			
		||||
		resp.ERROR(c, "error with create order: "+res.Error.Error())
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// PayJs 单独处理,只能用官方生成的二维码
 | 
			
		||||
	if data.PayWay == "payjs" {
 | 
			
		||||
		params := payment.JPayReq{
 | 
			
		||||
			TotalFee:   int(math.Ceil(order.Amount * 100)),
 | 
			
		||||
			OutTradeNo: order.OrderNo,
 | 
			
		||||
			Subject:    product.Name,
 | 
			
		||||
		}
 | 
			
		||||
		r := h.js.Pay(params)
 | 
			
		||||
		if r.IsOK() {
 | 
			
		||||
			resp.SUCCESS(c, gin.H{"order_no": order.OrderNo, "image": r.Qrcode})
 | 
			
		||||
			return
 | 
			
		||||
		} else {
 | 
			
		||||
			resp.ERROR(c, "error with generating payment qrcode: "+r.ReturnMsg)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var logo string
 | 
			
		||||
	if data.PayWay == "alipay" {
 | 
			
		||||
		logo = "res/img/alipay.jpg"
 | 
			
		||||
	} else if data.PayWay == "hupi" {
 | 
			
		||||
		if h.App.Config.HuPiPayConfig.Name == "wechat" {
 | 
			
		||||
			logo = "res/img/wechat-pay.jpg"
 | 
			
		||||
		} else {
 | 
			
		||||
			logo = "res/img/alipay.jpg"
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	file, err := h.fs.Open(logo)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		resp.ERROR(c, "error with open qrcode log file: "+err.Error())
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	parse, err := url.Parse(notifyURL)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		resp.ERROR(c, err.Error())
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	imageURL := fmt.Sprintf("%s://%s/api/payment/doPay?order_no=%s&pay_way=%s", parse.Scheme, parse.Host, orderNo, data.PayWay)
 | 
			
		||||
	imgData, err := utils.GenQrcode(imageURL, 400, file)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		resp.ERROR(c, err.Error())
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	imgDataBase64 := base64.StdEncoding.EncodeToString(imgData)
 | 
			
		||||
	resp.SUCCESS(c, gin.H{"order_no": orderNo, "image": fmt.Sprintf("data:image/jpg;base64, %s", imgDataBase64), "url": imageURL})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 异步通知回调公共逻辑
 | 
			
		||||
func (h *PaymentHandler) notify(orderNo string, tradeNo string) error {
 | 
			
		||||
	var order model.Order
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
VUE_APP_API_HOST=http://localhost:5678
 | 
			
		||||
VUE_APP_WS_HOST=ws://localhost:5678
 | 
			
		||||
VUE_APP_API_HOST=http://172.22.11.200:5678
 | 
			
		||||
VUE_APP_WS_HOST=ws://172.22.11.200:5678
 | 
			
		||||
VUE_APP_USER=18575670125
 | 
			
		||||
VUE_APP_PASS=12345678
 | 
			
		||||
VUE_APP_ADMIN_USER=admin
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										723
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										723
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -40,7 +40,8 @@
 | 
			
		||||
    "eslint": "^7.32.0",
 | 
			
		||||
    "eslint-plugin-vue": "^8.0.3",
 | 
			
		||||
    "stylus": "^0.58.1",
 | 
			
		||||
    "stylus-loader": "^7.0.0"
 | 
			
		||||
    "stylus-loader": "^7.0.0",
 | 
			
		||||
    "webpack": "^5.90.3"
 | 
			
		||||
  },
 | 
			
		||||
  "eslintConfig": {
 | 
			
		||||
    "root": true,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										7627
									
								
								web/pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										7627
									
								
								web/pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -7,6 +7,26 @@
 | 
			
		||||
<script setup>
 | 
			
		||||
import {ElConfigProvider} from 'element-plus';
 | 
			
		||||
import zhCn from 'element-plus/es/locale/lang/zh-cn';
 | 
			
		||||
 | 
			
		||||
const debounce = (fn, delay) => {
 | 
			
		||||
  let timer
 | 
			
		||||
  return (...args) => {
 | 
			
		||||
    if (timer) {
 | 
			
		||||
      clearTimeout(timer)
 | 
			
		||||
    }
 | 
			
		||||
    timer = setTimeout(() => {
 | 
			
		||||
      fn(...args)
 | 
			
		||||
    }, delay)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const _ResizeObserver = window.ResizeObserver;
 | 
			
		||||
window.ResizeObserver = class ResizeObserver extends _ResizeObserver {
 | 
			
		||||
  constructor(callback) {
 | 
			
		||||
    callback = debounce(callback, 200);
 | 
			
		||||
    super(callback);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3,12 +3,21 @@
 | 
			
		||||
      class="login-dialog"
 | 
			
		||||
      v-model="showDialog"
 | 
			
		||||
      :close-on-click-modal="true"
 | 
			
		||||
      :show-close="true"
 | 
			
		||||
      :show-close="false"
 | 
			
		||||
      :before-close="close"
 | 
			
		||||
      :width="400"
 | 
			
		||||
      title="用户登录"
 | 
			
		||||
  >
 | 
			
		||||
    <div class="form">
 | 
			
		||||
    <template #header="{ close, titleId, titleClass }">
 | 
			
		||||
      <div class="header">
 | 
			
		||||
        <div class="title">用户登录</div>
 | 
			
		||||
        <div class="close-icon">
 | 
			
		||||
          <el-icon>
 | 
			
		||||
            <Close/>
 | 
			
		||||
          </el-icon>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </template>
 | 
			
		||||
 | 
			
		||||
    <div class="login-box">
 | 
			
		||||
      <el-form label-width="75px">
 | 
			
		||||
        <el-form-item>
 | 
			
		||||
          <template #label>
 | 
			
		||||
@@ -20,7 +29,7 @@
 | 
			
		||||
            </div>
 | 
			
		||||
          </template>
 | 
			
		||||
          <template #default>
 | 
			
		||||
            <el-input v-model="username" size="large" placeholder="手机号码"/>
 | 
			
		||||
            <el-input v-model="data.username" size="large" placeholder="账号"/>
 | 
			
		||||
          </template>
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
        <el-form-item>
 | 
			
		||||
@@ -33,15 +42,100 @@
 | 
			
		||||
            </div>
 | 
			
		||||
          </template>
 | 
			
		||||
          <template #default>
 | 
			
		||||
            <el-input v-model="password" type="password" size="large" placeholder="密码"/>
 | 
			
		||||
            <el-input v-model="data.password" type="password" size="large" placeholder="密码"/>
 | 
			
		||||
          </template>
 | 
			
		||||
        </el-form-item>
 | 
			
		||||
 | 
			
		||||
        <div class="login-btn">
 | 
			
		||||
          <el-button type="primary" @click="submit" size="large" round>登录</el-button>
 | 
			
		||||
          <el-button plain @click="submit" size="large" round>注册</el-button>
 | 
			
		||||
        </div>
 | 
			
		||||
      </el-form>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="register-box">
 | 
			
		||||
      <el-form :model="data" label-width="120px" ref="formRef">
 | 
			
		||||
        <div class="block">
 | 
			
		||||
          <el-input placeholder="账号"
 | 
			
		||||
                    size="large"
 | 
			
		||||
                    v-model="data.username"
 | 
			
		||||
                    autocomplete="off">
 | 
			
		||||
            <template #prefix>
 | 
			
		||||
              <el-icon>
 | 
			
		||||
                <Iphone/>
 | 
			
		||||
              </el-icon>
 | 
			
		||||
            </template>
 | 
			
		||||
          </el-input>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="block">
 | 
			
		||||
          <el-input placeholder="请输入密码(8-16位)"
 | 
			
		||||
                    maxlength="16" size="large"
 | 
			
		||||
                    v-model="data.password" show-password
 | 
			
		||||
                    autocomplete="off">
 | 
			
		||||
            <template #prefix>
 | 
			
		||||
              <el-icon>
 | 
			
		||||
                <Lock/>
 | 
			
		||||
              </el-icon>
 | 
			
		||||
            </template>
 | 
			
		||||
          </el-input>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="block">
 | 
			
		||||
          <el-input placeholder="重复密码(8-16位)"
 | 
			
		||||
                    size="large" maxlength="16" v-model="data.repass" show-password
 | 
			
		||||
                    autocomplete="off">
 | 
			
		||||
            <template #prefix>
 | 
			
		||||
              <el-icon>
 | 
			
		||||
                <Lock/>
 | 
			
		||||
              </el-icon>
 | 
			
		||||
            </template>
 | 
			
		||||
          </el-input>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="block">
 | 
			
		||||
          <el-row :gutter="10">
 | 
			
		||||
            <el-col :span="12">
 | 
			
		||||
              <el-input placeholder="验证码"
 | 
			
		||||
                        size="large" maxlength="30"
 | 
			
		||||
                        v-model="data.code"
 | 
			
		||||
                        autocomplete="off">
 | 
			
		||||
                <template #prefix>
 | 
			
		||||
                  <el-icon>
 | 
			
		||||
                    <Checked/>
 | 
			
		||||
                  </el-icon>
 | 
			
		||||
                </template>
 | 
			
		||||
              </el-input>
 | 
			
		||||
            </el-col>
 | 
			
		||||
            <el-col :span="12">
 | 
			
		||||
              <send-msg size="large" :receiver="data.username"/>
 | 
			
		||||
            </el-col>
 | 
			
		||||
          </el-row>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="block">
 | 
			
		||||
          <el-input placeholder="邀请码(可选)"
 | 
			
		||||
                    size="large"
 | 
			
		||||
                    v-model="data.invite_code"
 | 
			
		||||
                    autocomplete="off">
 | 
			
		||||
            <template #prefix>
 | 
			
		||||
              <el-icon>
 | 
			
		||||
                <Message/>
 | 
			
		||||
              </el-icon>
 | 
			
		||||
            </template>
 | 
			
		||||
          </el-input>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <el-row class="btn-row">
 | 
			
		||||
          <el-button class="login-btn" type="primary" @click="">注册</el-button>
 | 
			
		||||
        </el-row>
 | 
			
		||||
 | 
			
		||||
        <el-row class="text-line">
 | 
			
		||||
          已经有账号?
 | 
			
		||||
          <el-link type="primary" @click="">登录</el-link>
 | 
			
		||||
        </el-row>
 | 
			
		||||
      </el-form>
 | 
			
		||||
    </div>
 | 
			
		||||
  </el-dialog>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
@@ -51,7 +145,8 @@ import {httpPost} from "@/utils/http";
 | 
			
		||||
import {ElMessage} from "element-plus";
 | 
			
		||||
import {setUserToken} from "@/store/session";
 | 
			
		||||
import {validateMobile} from "@/utils/validate";
 | 
			
		||||
import {Lock, User} from "@element-plus/icons-vue";
 | 
			
		||||
import {Checked, Close, Iphone, Lock, Message, Position, User} from "@element-plus/icons-vue";
 | 
			
		||||
import SendMsg from "@/components/SendMsg.vue";
 | 
			
		||||
 | 
			
		||||
// eslint-disable-next-line no-undef
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
@@ -60,19 +155,14 @@ const props = defineProps({
 | 
			
		||||
const showDialog = computed(() => {
 | 
			
		||||
  return props.show
 | 
			
		||||
})
 | 
			
		||||
const username = ref("")
 | 
			
		||||
const password = ref("")
 | 
			
		||||
const data = ref({
 | 
			
		||||
  username: "",
 | 
			
		||||
  password: ""
 | 
			
		||||
})
 | 
			
		||||
// eslint-disable-next-line no-undef
 | 
			
		||||
const emits = defineEmits(['hide']);
 | 
			
		||||
const submit = function () {
 | 
			
		||||
  if (!validateMobile(username.value)) {
 | 
			
		||||
    return ElMessage.error('请输入合法的手机号');
 | 
			
		||||
  }
 | 
			
		||||
  if (password.value.trim() === '') {
 | 
			
		||||
    return ElMessage.error('请输入密码');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  httpPost('/api/user/login', {username: username.value.trim(), password: password.value.trim()}).then((res) => {
 | 
			
		||||
  httpPost('/api/user/login', data.value).then((res) => {
 | 
			
		||||
    setUserToken(res.data)
 | 
			
		||||
    ElMessage.success("登录成功!")
 | 
			
		||||
    emits("hide")
 | 
			
		||||
@@ -87,30 +177,58 @@ const close = function () {
 | 
			
		||||
 | 
			
		||||
<style lang="stylus">
 | 
			
		||||
.login-dialog {
 | 
			
		||||
  border-radius 20px
 | 
			
		||||
  border-radius 10px
 | 
			
		||||
  max-width 600px
 | 
			
		||||
 | 
			
		||||
  .label {
 | 
			
		||||
    padding-top 3px
 | 
			
		||||
  .header {
 | 
			
		||||
    position relative
 | 
			
		||||
 | 
			
		||||
    .el-icon {
 | 
			
		||||
      position relative
 | 
			
		||||
    .title {
 | 
			
		||||
      padding 0
 | 
			
		||||
      font-size 18px
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .close-icon {
 | 
			
		||||
      cursor pointer
 | 
			
		||||
      position absolute
 | 
			
		||||
      right 0
 | 
			
		||||
      top 0
 | 
			
		||||
      font-weight normal
 | 
			
		||||
      font-size 20px
 | 
			
		||||
      margin-right 6px
 | 
			
		||||
      top 4px
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    span {
 | 
			
		||||
      font-size 16px
 | 
			
		||||
      &:hover {
 | 
			
		||||
        color #20a0ff
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .login-btn {
 | 
			
		||||
    text-align center
 | 
			
		||||
    padding-top 10px
 | 
			
		||||
  .login-box {
 | 
			
		||||
    .label {
 | 
			
		||||
      padding-top 3px
 | 
			
		||||
 | 
			
		||||
    .el-button {
 | 
			
		||||
      width 50%
 | 
			
		||||
      .el-icon {
 | 
			
		||||
        position relative
 | 
			
		||||
        font-size 20px
 | 
			
		||||
        margin-right 6px
 | 
			
		||||
        top 4px
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      span {
 | 
			
		||||
        font-size 16px
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .login-btn {
 | 
			
		||||
      display flex
 | 
			
		||||
      padding-top 10px
 | 
			
		||||
      justify-content center
 | 
			
		||||
 | 
			
		||||
      .el-button {
 | 
			
		||||
        font-size 16px
 | 
			
		||||
        width 100px
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
@@ -237,6 +237,8 @@
 | 
			
		||||
    </el-dialog>
 | 
			
		||||
 | 
			
		||||
    <config-dialog v-if="isLogin" :show="showConfigDialog" :models="models" @hide="showConfigDialog = false"/>
 | 
			
		||||
 | 
			
		||||
    <login-dialog :show="true"/>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -271,6 +273,7 @@ import {checkSession} from "@/action/session";
 | 
			
		||||
import Welcome from "@/components/Welcome.vue";
 | 
			
		||||
import ChatMidJourney from "@/components/ChatMidJourney.vue";
 | 
			
		||||
import FileSelect from "@/components/FileSelect.vue";
 | 
			
		||||
import LoginDialog from "@/components/LoginDialog.vue";
 | 
			
		||||
 | 
			
		||||
const title = ref('ChatGPT-智能助手');
 | 
			
		||||
const models = ref([])
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user