小程序初始提交
This commit is contained in:
134
cool-unix/pages/user/components/login/phone.uvue
Normal file
134
cool-unix/pages/user/components/login/phone.uvue
Normal file
@@ -0,0 +1,134 @@
|
||||
<template>
|
||||
<view class="flex flex-col mb-5">
|
||||
<cl-text :pt="{ className: 'text-lg font-bold' }">{{ t("手机登录") }}</cl-text>
|
||||
<cl-text :pt="{ className: 'text-sm mt-2' }" color="info">{{
|
||||
t("未注册的手机号登录成功后将自动注册")
|
||||
}}</cl-text>
|
||||
</view>
|
||||
|
||||
<view class="flex flex-col">
|
||||
<view class="mb-3 flex flex-row">
|
||||
<cl-input
|
||||
v-model="form.phone"
|
||||
prefix-icon="device-fill"
|
||||
:placeholder="t('请输入手机号')"
|
||||
:border="false"
|
||||
:pt="{
|
||||
className: parseClass([
|
||||
'!h-[90rpx] flex-1 !rounded-xl !px-4',
|
||||
[isDark, '!bg-surface-70', '!bg-white']
|
||||
]),
|
||||
prefixIcon: {
|
||||
className: 'mr-1'
|
||||
}
|
||||
}"
|
||||
></cl-input>
|
||||
</view>
|
||||
|
||||
<view class="relative flex flex-row items-center mb-5">
|
||||
<cl-input
|
||||
v-model="form.smsCode"
|
||||
:clearable="false"
|
||||
type="number"
|
||||
prefix-icon="shield-check-fill"
|
||||
:placeholder="t('请输入验证码')"
|
||||
:maxlength="4"
|
||||
:border="false"
|
||||
:pt="{
|
||||
className: parseClass([
|
||||
'!h-[90rpx] flex-1 !rounded-xl !px-4',
|
||||
[isDark, '!bg-surface-70', '!bg-white']
|
||||
]),
|
||||
prefixIcon: {
|
||||
className: 'mr-1'
|
||||
}
|
||||
}"
|
||||
>
|
||||
</cl-input>
|
||||
|
||||
<view class="absolute right-0">
|
||||
<sms-btn
|
||||
:ref="refs.set('smsBtn')"
|
||||
:phone="form.phone"
|
||||
@success="showCode = true"
|
||||
></sms-btn>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<cl-button
|
||||
:pt="{
|
||||
className: '!h-[90rpx] !rounded-xl'
|
||||
}"
|
||||
:loading="loading"
|
||||
:disabled="disabled"
|
||||
@tap="toLogin"
|
||||
>
|
||||
{{ t("登录") }}
|
||||
</cl-button>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { t } from "@/locale";
|
||||
import { computed, inject, ref, type PropType } from "vue";
|
||||
import type { LoginForm } from "../../types";
|
||||
import SmsBtn from "@/components/sms-btn.uvue";
|
||||
import { isDark, parseClass, request, useRefs, type Response } from "@/cool";
|
||||
import { useUi } from "@/uni_modules/cool-ui";
|
||||
|
||||
const props = defineProps({
|
||||
form: {
|
||||
type: Object as PropType<LoginForm>,
|
||||
default: () => ({})
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(["success"]);
|
||||
|
||||
const ui = useUi();
|
||||
const refs = useRefs();
|
||||
|
||||
// 是否同意
|
||||
const isAgree = inject("isAgree") as () => boolean;
|
||||
|
||||
// 是否显示验证码
|
||||
const showCode = ref(false);
|
||||
|
||||
// 是否加载中
|
||||
const loading = ref(false);
|
||||
|
||||
// 是否禁用
|
||||
const disabled = computed(() => {
|
||||
return props.form.phone == "" || props.form.smsCode == "";
|
||||
});
|
||||
|
||||
// 登录
|
||||
async function toLogin() {
|
||||
if (!isAgree()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { phone, smsCode } = props.form;
|
||||
|
||||
loading.value = true;
|
||||
|
||||
await request({
|
||||
url: "/app/user/login/phone",
|
||||
method: "POST",
|
||||
data: {
|
||||
phone,
|
||||
smsCode
|
||||
}
|
||||
})
|
||||
.then((res) => {
|
||||
emit("success", res);
|
||||
})
|
||||
.catch((err) => {
|
||||
ui.showToast({
|
||||
message: (err as Response).message!
|
||||
});
|
||||
});
|
||||
|
||||
loading.value = false;
|
||||
}
|
||||
</script>
|
||||
242
cool-unix/pages/user/components/login/wx.uvue
Normal file
242
cool-unix/pages/user/components/login/wx.uvue
Normal file
@@ -0,0 +1,242 @@
|
||||
<template>
|
||||
<cl-popup
|
||||
v-model="editVisible"
|
||||
direction="center"
|
||||
:title="t('提示')"
|
||||
size="80%"
|
||||
@close="onEditClose"
|
||||
>
|
||||
<view class="p-4 pt-0">
|
||||
<cl-text color="info" :pt="{ className: 'text-sm' }">
|
||||
{{ t("为提供更好的服务,我们邀请您填写昵称、头像等公开信息") }}
|
||||
</cl-text>
|
||||
|
||||
<view
|
||||
class="flex flex-row justify-between items-center bg-surface-100 rounded-xl p-2 px-3 mt-3 h-[95rpx]"
|
||||
>
|
||||
<cl-text>{{ t("头像") }}</cl-text>
|
||||
|
||||
<view class="relative">
|
||||
<cl-avatar :size="60" :src="editForm.avatarUrl"></cl-avatar>
|
||||
|
||||
<button
|
||||
class="absolute top-0 right-0 h-10 w-10 z-10 opacity-0 p-0 m-0"
|
||||
open-type="chooseAvatar"
|
||||
@chooseavatar="onEditChooseAvatar"
|
||||
></button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="flex flex-row justify-between items-center bg-surface-100 rounded-xl p-2 px-3 mt-3 h-[95rpx]"
|
||||
>
|
||||
<cl-text>{{ t("昵称") }}</cl-text>
|
||||
|
||||
<cl-input
|
||||
v-model="editForm.nickName"
|
||||
type="nickname"
|
||||
:border="false"
|
||||
:placeholder="t('点击输入昵称')"
|
||||
:maxlength="16"
|
||||
:pt="{
|
||||
className: '!bg-transparent !px-0 flex-1',
|
||||
inner: {
|
||||
className: 'text-right'
|
||||
}
|
||||
}"
|
||||
></cl-input>
|
||||
</view>
|
||||
|
||||
<view class="flex flex-row mt-4">
|
||||
<cl-button
|
||||
size="large"
|
||||
text
|
||||
border
|
||||
type="light"
|
||||
:pt="{
|
||||
className: 'flex-1 !rounded-xl h-[80rpx]'
|
||||
}"
|
||||
@tap="editClose"
|
||||
>{{ t("取消") }}</cl-button
|
||||
>
|
||||
<cl-button
|
||||
size="large"
|
||||
:pt="{
|
||||
className: 'flex-1 !rounded-xl h-[80rpx]'
|
||||
}"
|
||||
:loading="editLoading"
|
||||
@tap="editSave"
|
||||
>{{ t("确认") }}</cl-button
|
||||
>
|
||||
</view>
|
||||
</view>
|
||||
</cl-popup>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
parse,
|
||||
request,
|
||||
router,
|
||||
upload,
|
||||
userInfo,
|
||||
useStore,
|
||||
useWx,
|
||||
type Response,
|
||||
type Token
|
||||
} from "@/cool";
|
||||
import { t } from "@/locale";
|
||||
import { useUi } from "@/uni_modules/cool-ui";
|
||||
import { reactive, ref } from "vue";
|
||||
|
||||
const emit = defineEmits(["success"]);
|
||||
|
||||
const { user } = useStore();
|
||||
const ui = useUi();
|
||||
const wx = useWx();
|
||||
|
||||
// 是否显示编辑
|
||||
const editVisible = ref(false);
|
||||
|
||||
// 是否保存中
|
||||
const editLoading = ref(false);
|
||||
|
||||
// 编辑表单
|
||||
type EditForm = {
|
||||
avatarUrl: string;
|
||||
nickName: string;
|
||||
};
|
||||
const editForm = reactive<EditForm>({
|
||||
avatarUrl: "",
|
||||
nickName: ""
|
||||
});
|
||||
|
||||
// 编辑打开
|
||||
function editOpen() {
|
||||
editVisible.value = true;
|
||||
}
|
||||
|
||||
// 编辑关闭
|
||||
function editClose() {
|
||||
editVisible.value = false;
|
||||
}
|
||||
|
||||
// 编辑保存
|
||||
async function editSave() {
|
||||
// 校验头像是否已上传
|
||||
if (editForm.avatarUrl == "") {
|
||||
ui.showToast({
|
||||
message: t("请上传头像")
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 校验昵称是否已填写
|
||||
if (editForm.nickName == "") {
|
||||
ui.showToast({
|
||||
message: t("请输入昵称")
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置保存状态为加载中
|
||||
editLoading.value = true;
|
||||
|
||||
// 上传头像并更新用户信息
|
||||
await upload(editForm.avatarUrl)
|
||||
.then((url) => {
|
||||
// 上传成功后,更新用户昵称和头像
|
||||
user.update({
|
||||
nickName: editForm.nickName,
|
||||
avatarUrl: url
|
||||
});
|
||||
|
||||
// 关闭弹窗
|
||||
editClose();
|
||||
|
||||
// 跳转首页
|
||||
router.nextLogin();
|
||||
})
|
||||
.catch((err) => {
|
||||
// 上传失败,提示错误信息
|
||||
ui.showToast({
|
||||
message: (err as Response).message!
|
||||
});
|
||||
});
|
||||
|
||||
// 恢复保存状态
|
||||
editLoading.value = false;
|
||||
}
|
||||
|
||||
// 编辑选择头像
|
||||
function onEditChooseAvatar(e: UniEvent) {
|
||||
// #ifdef MP-WEIXIN
|
||||
editForm.avatarUrl = e.detail.avatarUrl;
|
||||
// #endif
|
||||
}
|
||||
|
||||
// 编辑关闭
|
||||
function onEditClose() {
|
||||
editVisible.value = false;
|
||||
}
|
||||
|
||||
// 微信小程序登录
|
||||
async function miniLogin() {
|
||||
// #ifdef MP
|
||||
ui.showLoading(t("登录中"));
|
||||
|
||||
await wx.miniLogin().then(async (data) => {
|
||||
await request({
|
||||
url: "/app/user/login/mini",
|
||||
method: "POST",
|
||||
data
|
||||
})
|
||||
.then(async (res) => {
|
||||
// 设置token
|
||||
user.setToken(parse<Token>(res)!);
|
||||
|
||||
// 获取用户信息
|
||||
await user.get();
|
||||
|
||||
// 是否首次注册,根据业务情况调整判断逻辑
|
||||
if (userInfo.value?.nickName == "微信用户") {
|
||||
// 打开编辑弹窗
|
||||
editOpen();
|
||||
} else {
|
||||
// 跳转首页
|
||||
router.nextLogin();
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
ui.showToast({
|
||||
message: (err as Response).message!
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
ui.hideLoading();
|
||||
// #endif
|
||||
}
|
||||
|
||||
// 微信APP登录
|
||||
function appLogin() {
|
||||
// 开发中
|
||||
}
|
||||
|
||||
// 微信登录
|
||||
async function login() {
|
||||
// #ifdef MP
|
||||
miniLogin();
|
||||
// #endif
|
||||
|
||||
// #ifdef APP
|
||||
appLogin();
|
||||
// #endif
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
login,
|
||||
editOpen,
|
||||
editClose
|
||||
});
|
||||
</script>
|
||||
52
cool-unix/pages/user/edit-description.uvue
Normal file
52
cool-unix/pages/user/edit-description.uvue
Normal file
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<cl-topbar safe-area-top :title="t('编辑简介')" background-color="transparent"> </cl-topbar>
|
||||
|
||||
<view class="p-3">
|
||||
<cl-textarea
|
||||
v-model="content"
|
||||
:placeholder="t('介绍一下自己')"
|
||||
:border="false"
|
||||
:height="200"
|
||||
>
|
||||
</cl-textarea>
|
||||
</view>
|
||||
|
||||
<cl-footer>
|
||||
<cl-button size="large" :disabled="content == ''" @tap="confirm">{{
|
||||
t("确认")
|
||||
}}</cl-button>
|
||||
</cl-footer>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { router, userInfo, useStore } from "@/cool";
|
||||
import { t } from "@/locale";
|
||||
import { useUi } from "@/uni_modules/cool-ui";
|
||||
import { ref } from "vue";
|
||||
|
||||
const ui = useUi();
|
||||
const { user } = useStore();
|
||||
|
||||
// 输入框内容
|
||||
const content = ref("");
|
||||
|
||||
async function confirm() {
|
||||
if (content.value == "") {
|
||||
return ui.showToast({
|
||||
message: t("简介不能为空")
|
||||
});
|
||||
}
|
||||
|
||||
await user.update({
|
||||
description: content.value
|
||||
});
|
||||
|
||||
router.back();
|
||||
}
|
||||
|
||||
onReady(() => {
|
||||
content.value = userInfo.value?.description ?? "";
|
||||
});
|
||||
</script>
|
||||
80
cool-unix/pages/user/edit-name.uvue
Normal file
80
cool-unix/pages/user/edit-name.uvue
Normal file
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<cl-topbar safe-area-top :title="t('编辑昵称')" background-color="transparent"> </cl-topbar>
|
||||
|
||||
<view class="p-3">
|
||||
<cl-input
|
||||
v-model="content"
|
||||
autofocus
|
||||
:placeholder="t('请输入昵称')"
|
||||
:border="false"
|
||||
:pt="{
|
||||
className: '!h-[80rpx]'
|
||||
}"
|
||||
>
|
||||
<template #append>
|
||||
<cl-text color="info" :pt="{ className: 'text-sm ml-2' }"
|
||||
>{{ content.length }}/20</cl-text
|
||||
>
|
||||
</template>
|
||||
</cl-input>
|
||||
|
||||
<view class="p-3">
|
||||
<cl-text color="info" :pt="{ className: 'text-sm' }">{{
|
||||
t("请设置2-20个字符,不包括@<>/等无效字符")
|
||||
}}</cl-text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<cl-footer>
|
||||
<cl-button size="large" :disabled="content == ''" @tap="confirm">{{
|
||||
t("确认")
|
||||
}}</cl-button>
|
||||
</cl-footer>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { router, userInfo, useStore } from "@/cool";
|
||||
import { t } from "@/locale";
|
||||
import { useUi } from "@/uni_modules/cool-ui";
|
||||
import { ref } from "vue";
|
||||
|
||||
const ui = useUi();
|
||||
const { user } = useStore();
|
||||
|
||||
// 输入框内容
|
||||
const content = ref("");
|
||||
|
||||
// 确认按钮点击事件
|
||||
async function confirm() {
|
||||
// 检查昵称长度和特殊字符
|
||||
if (content.value.length < 2 || content.value.length > 20) {
|
||||
ui.showToast({
|
||||
message: t("昵称长度需在2-20个字符之间")
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 正则匹配 - 不允许特殊字符@<>/等
|
||||
const reg = /^[^@<>\/]*$/;
|
||||
if (!reg.test(content.value)) {
|
||||
ui.showToast({
|
||||
message: t("昵称不能包含@<>/等特殊字符")
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新用户昵称
|
||||
await user.update({
|
||||
nickName: content.value
|
||||
});
|
||||
|
||||
router.back();
|
||||
}
|
||||
|
||||
// 页面加载时,设置输入框内容
|
||||
onReady(() => {
|
||||
content.value = userInfo.value?.nickName ?? "";
|
||||
});
|
||||
</script>
|
||||
247
cool-unix/pages/user/edit.uvue
Normal file
247
cool-unix/pages/user/edit.uvue
Normal file
@@ -0,0 +1,247 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<view class="p-3">
|
||||
<view class="flex flex-col justify-center items-center py-10 mb-3">
|
||||
<view class="relative overflow-visible">
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<button
|
||||
class="absolute top-0 left-0 w-full h-full opacity-0 z-10"
|
||||
open-type="chooseAvatar"
|
||||
@chooseavatar="uploadAvatar"
|
||||
></button>
|
||||
<!-- #endif -->
|
||||
|
||||
<cl-avatar
|
||||
:src="userInfo?.avatarUrl"
|
||||
:size="150"
|
||||
:pt="{ className: '!rounded-3xl', icon: { size: 60 } }"
|
||||
@tap="uploadAvatar"
|
||||
>
|
||||
</cl-avatar>
|
||||
|
||||
<view
|
||||
class="flex flex-col justify-center items-center absolute bottom-0 right-[-6rpx] bg-black rounded-full p-1 border border-solid border-white"
|
||||
>
|
||||
<cl-icon name="edit-line" color="white" :size="24"></cl-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<cl-list :pt="{ className: 'mb-3' }">
|
||||
<cl-list-item
|
||||
:label="t('我的昵称')"
|
||||
hoverable
|
||||
arrow
|
||||
justify="start"
|
||||
@tap="router.to('/pages/user/edit-name')"
|
||||
>
|
||||
<cl-text>{{ userInfo?.nickName }}</cl-text>
|
||||
</cl-list-item>
|
||||
<cl-list-item label="手机号" hoverable justify="start">
|
||||
<cl-text>{{ userInfo?.phone }}</cl-text>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
|
||||
<cl-list :pt="{ className: 'mb-3' }">
|
||||
<cl-list-item
|
||||
:label="t('简介')"
|
||||
hoverable
|
||||
arrow
|
||||
justify="start"
|
||||
@tap="router.to('/pages/user/edit-description')"
|
||||
>
|
||||
<cl-text color="info" v-if="userInfo?.description == null">{{
|
||||
t("介绍一下自己")
|
||||
}}</cl-text>
|
||||
<cl-text ellipsis v-else>{{ userInfo?.description }}</cl-text>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
|
||||
<cl-list :pt="{ className: 'mb-3' }">
|
||||
<cl-list-item
|
||||
:label="t('性别')"
|
||||
hoverable
|
||||
arrow
|
||||
justify="start"
|
||||
@tap="open('gender')"
|
||||
>
|
||||
<cl-text>{{ genderText }}</cl-text>
|
||||
<cl-text color="info" v-if="genderText == ''">{{ t("编辑性别") }}</cl-text>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item
|
||||
:label="t('生日')"
|
||||
hoverable
|
||||
arrow
|
||||
justify="start"
|
||||
@tap="open('birthday')"
|
||||
>
|
||||
<cl-text>{{ userInfo?.birthday }}</cl-text>
|
||||
<cl-text color="info" v-if="userInfo?.birthday == null">{{
|
||||
t("选择生日")
|
||||
}}</cl-text>
|
||||
</cl-list-item>
|
||||
|
||||
<cl-list-item
|
||||
:label="t('地区')"
|
||||
hoverable
|
||||
arrow
|
||||
justify="start"
|
||||
@tap="open('region')"
|
||||
>
|
||||
<cl-text>{{ regionText }}</cl-text>
|
||||
<cl-text color="info" v-if="regionText == ''">{{
|
||||
t("选择所在的地区")
|
||||
}}</cl-text>
|
||||
</cl-list-item>
|
||||
</cl-list>
|
||||
|
||||
<cl-select
|
||||
:title="t('选择性别')"
|
||||
:model-value="userInfo?.gender"
|
||||
:ref="refs.set('gender')"
|
||||
:options="genderOptions"
|
||||
:show-trigger="false"
|
||||
@change="onGenderChange"
|
||||
></cl-select>
|
||||
|
||||
<cl-select-date
|
||||
:title="t('选择生日')"
|
||||
:model-value="userInfo?.birthday"
|
||||
:ref="refs.set('birthday')"
|
||||
type="date"
|
||||
:end="today"
|
||||
:show-trigger="false"
|
||||
@change="onBirthdayChange"
|
||||
></cl-select-date>
|
||||
|
||||
<cl-cascader
|
||||
:title="t('选择所在的地区')"
|
||||
:ref="refs.set('region')"
|
||||
:options="regionOptions"
|
||||
:show-trigger="false"
|
||||
@change="onRegionChange"
|
||||
></cl-cascader>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { dayUts, router, upload, useRefs, useStore, type Response, userInfo } from "@/cool";
|
||||
import { t } from "@/locale";
|
||||
import { useCascader, useUi, type ClSelectOption } from "@/uni_modules/cool-ui";
|
||||
import { computed, ref } from "vue";
|
||||
import pca from "@/data/pca.json";
|
||||
|
||||
const { user } = useStore();
|
||||
const ui = useUi();
|
||||
const refs = useRefs();
|
||||
|
||||
// 今天
|
||||
const today = dayUts().format("YYYY-MM-DD");
|
||||
|
||||
// 性别选项
|
||||
const genderOptions = ref<ClSelectOption[]>([
|
||||
{
|
||||
label: t("保密"),
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
label: t("男"),
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: t("女"),
|
||||
value: 2
|
||||
}
|
||||
]);
|
||||
|
||||
// 性别文本
|
||||
const genderText = computed(() => {
|
||||
return [t("保密"), t("男"), t("女")][userInfo.value?.gender!];
|
||||
});
|
||||
|
||||
// 性别改变
|
||||
function onGenderChange(val: number) {
|
||||
user.update({
|
||||
gender: val
|
||||
});
|
||||
|
||||
ui.showToast({
|
||||
message: t("性别设置成功")
|
||||
});
|
||||
}
|
||||
|
||||
// 生日改变
|
||||
function onBirthdayChange(val: string) {
|
||||
user.update({
|
||||
birthday: val
|
||||
});
|
||||
|
||||
ui.showToast({
|
||||
message: t("生日设置成功")
|
||||
});
|
||||
}
|
||||
|
||||
// 地区选项
|
||||
const regionOptions = useCascader(pca);
|
||||
|
||||
// 地区文本
|
||||
const regionText = computed(() => {
|
||||
return [userInfo.value?.province, userInfo.value?.city, userInfo.value?.district]
|
||||
.filter((e) => e != null)
|
||||
.join(" - ");
|
||||
});
|
||||
|
||||
// 地区改变
|
||||
function onRegionChange(arr: string[]) {
|
||||
user.update({
|
||||
province: arr[0],
|
||||
city: arr[1],
|
||||
district: arr[2]
|
||||
});
|
||||
|
||||
ui.showToast({
|
||||
message: t("地区设置成功")
|
||||
});
|
||||
}
|
||||
|
||||
// 打开弹窗
|
||||
function open(name: string) {
|
||||
refs.open(name);
|
||||
}
|
||||
|
||||
// 上传头像
|
||||
function uploadAvatar(e: UniEvent) {
|
||||
function next(path: string) {
|
||||
upload(path)
|
||||
.then((url) => {
|
||||
ui.showToast({
|
||||
message: t("头像上传成功")
|
||||
});
|
||||
|
||||
user.update({
|
||||
avatarUrl: url
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
ui.showToast({
|
||||
message: (err as Response).message!
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
next(e.detail.avatarUrl);
|
||||
// #endif
|
||||
|
||||
// #ifndef MP-WEIXIN
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
success(res) {
|
||||
next(res.tempFiles[0].path);
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
}
|
||||
</script>
|
||||
130
cool-unix/pages/user/login.uvue
Normal file
130
cool-unix/pages/user/login.uvue
Normal file
@@ -0,0 +1,130 @@
|
||||
<template>
|
||||
<cl-page>
|
||||
<cl-topbar safe-area-top background-color="transparent"></cl-topbar>
|
||||
|
||||
<view class="px-10">
|
||||
<!-- Logo -->
|
||||
<view class="flex flex-col items-center justify-center py-20">
|
||||
<view class="p-3 bg-primary-500 rounded-2xl">
|
||||
<cl-image
|
||||
src="/static/logo.png"
|
||||
mode="widthFix"
|
||||
:width="80"
|
||||
:height="80"
|
||||
></cl-image>
|
||||
</view>
|
||||
|
||||
<cl-text :pt="{ className: 'text-xl font-bold mt-3' }">{{ config.name }}</cl-text>
|
||||
</view>
|
||||
|
||||
<!-- 手机号登录 -->
|
||||
<login-phone :form="form" @success="toLogin"></login-phone>
|
||||
|
||||
<!-- 微信登录 -->
|
||||
<login-wx :ref="refs.set('loginWx')"></login-wx>
|
||||
|
||||
<!-- 协议 -->
|
||||
<view class="mt-6 flex flex-row flex-wrap items-center justify-center">
|
||||
<cl-checkbox
|
||||
v-model="agree"
|
||||
:pt="{ icon: { size: 28 } }"
|
||||
active-icon="checkbox-circle-fill"
|
||||
inactive-icon="checkbox-blank-circle-line"
|
||||
>
|
||||
</cl-checkbox>
|
||||
<cl-text color="info" :pt="{ className: 'text-xs' }">{{
|
||||
t("已阅读并同意")
|
||||
}}</cl-text>
|
||||
<cl-text
|
||||
:pt="{ className: 'text-xs' }"
|
||||
@tap.stop="toDoc(t('用户协议'), 'userAgreement')"
|
||||
>
|
||||
《{{ t("用户协议") }}》
|
||||
</cl-text>
|
||||
<cl-text color="info" :pt="{ className: 'text-xs' }">、</cl-text>
|
||||
<cl-text
|
||||
:pt="{ className: 'text-xs' }"
|
||||
@tap.stop="toDoc(t('隐私政策'), 'privacyPolicy')"
|
||||
>
|
||||
《{{ t("隐私政策") }}》
|
||||
</cl-text>
|
||||
</view>
|
||||
|
||||
<!-- 第三方登录 -->
|
||||
<view class="flex flex-row justify-center mt-10 px-10">
|
||||
<view
|
||||
class="login-item"
|
||||
:class="{
|
||||
'is-dark': isDark
|
||||
}"
|
||||
@tap="refs.callMethod('loginWx', 'login')"
|
||||
>
|
||||
<cl-icon name="wechat-fill" :size="38" color="#00b223"></cl-icon>
|
||||
</view>
|
||||
|
||||
<view class="login-item" :class="{ 'is-dark': isDark }">
|
||||
<cl-icon name="apple-fill" :size="38"></cl-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</cl-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { config } from "@/config";
|
||||
import { isDark, parse, router, useRefs, useStore, type Token } from "@/cool";
|
||||
import { t } from "@/locale";
|
||||
import { provide, reactive, ref } from "vue";
|
||||
import type { LoginForm } from "./types";
|
||||
import { useUi } from "@/uni_modules/cool-ui";
|
||||
import LoginPhone from "./components/login/phone.uvue";
|
||||
import LoginWx from "./components/login/wx.uvue";
|
||||
|
||||
const { user } = useStore();
|
||||
const ui = useUi();
|
||||
const refs = useRefs();
|
||||
|
||||
// 表单
|
||||
const form = reactive<LoginForm>({
|
||||
phone: "13014591689",
|
||||
smsCode: "6666"
|
||||
});
|
||||
|
||||
// 是否同意
|
||||
const agree = ref(false);
|
||||
|
||||
// 登录成功
|
||||
async function toLogin(res: any) {
|
||||
user.setToken(parse<Token>(res)!);
|
||||
user.get();
|
||||
router.nextLogin();
|
||||
}
|
||||
|
||||
// 跳转文档
|
||||
function toDoc(name: string, path: string) {}
|
||||
|
||||
// 是否同意
|
||||
function isAgree() {
|
||||
if (!agree.value) {
|
||||
ui.showToast({
|
||||
message: t("请先阅读并同意《用户协议》和《隐私政策》")
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
provide("isAgree", isAgree);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.login-item {
|
||||
@apply mx-2 p-2 flex items-center justify-center rounded-full bg-white border border-solid border-surface-100;
|
||||
|
||||
&.is-dark {
|
||||
@apply border-surface-600 bg-surface-700;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
4
cool-unix/pages/user/types/index.ts
Normal file
4
cool-unix/pages/user/types/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export type LoginForm = {
|
||||
phone: string;
|
||||
smsCode: string;
|
||||
};
|
||||
Reference in New Issue
Block a user