This commit is contained in:
oneao committed 2026-03-18 17:29:39 +08:00
1 parent 68f08d921a
commit 3656d05f62
97 files changed
+17495

No files matched your search

@@ -0,0 +1,162 @@
<script setup lang="ts">
import type { UploadFile, UploadFileItem } from 'sard-uniapp'
import { dialog, isImageUrl, isVideoUrl, toast } from 'sard-uniapp'
import { aliyunpluploadApi, delSubRecordApi, loaddataAttachApi } from '@/request/api'
import { downloadFile, formatFileSize, imageCompress, isImage, uploadFileToAliyun } from '@/utils/file'
const fileList = ref<UploadFileItem[]>([])
const isEdit = ref(true)
const aliyunBucket = import.meta.env.VITE_ALIYUN_BUCKET
const father = ref('')
const moduleid = ref('')
const cateid = ref('')
onLoad((options) => {
father.value = options.father
moduleid.value = options.moduleid
cateid.value = options.cateid || ''
const edit = options.edit || '0'
isEdit.value = edit === '1'
nextTick(() => {
loadFileData()
})
})
async function loadFileData() {
let searchcondition = ` father = '${father.value}' and mx_moduleid = '${moduleid.value}'`
if (cateid.value !== '') {
searchcondition += ` and mx_cate_id = '${cateid.value}'`
}
loaddataAttachApi({
viewname: 'bf_files',
searchcondition,
orderfield: 'mx_xh',
soption: '',
}).then((res) => {
fileList.value = res.data.map((item) => {
return {
...item,
name: item.mx_filename,
url: item.mx_mapfilename,
}
})
console.log(fileList.value)
})
}
function onItemClick(fileItem: UploadFileItem) {
console.log(fileItem)
if (!isImageUrl(fileItem.mx_mapfilename) && !isVideoUrl(fileItem.mx_mapfilename)) {
downloadFile(fileItem.mx_mapfilename, fileItem.name)
}
}
function beforeRead(file: UploadFile): boolean | Promise<UploadFile> {
// 非图片直接允许上传
if (!isImage(file.name))
return true
// 图片异步压缩,返回修改后的 UploadFile 对象
return imageCompress(file.path).then((res) => {
console.log(res)
file.path = res.path
file.size = res.size
return file
}).catch((err) => {
console.error('图片压缩失败:', err)
return file
})
}
async function afterRead(fileItem: UploadFileItem) {
try {
// 设置上传中状态
fileItem.status = 'uploading'
fileItem.message = '正在上传'
fileList.value = [...fileList.value]
// 上传到阿里云
const res = await uploadFileToAliyun(
fileItem.file.path,
fileItem.file.name,
fileItem.file.type,
)
if (!res.url)
throw new Error('上传失败:未获取到文件 URL')
// 调用后端接口保存文件信息
const saveRes = await aliyunpluploadApi({
despathfile: res.url,
myfilename: fileItem.file.name,
size: formatFileSize(fileItem.file.size),
tablename: 'bf_files',
sid: father.value,
mx_moduleid: moduleid.value,
mx_cate_id: cateid.value,
})
if (saveRes.data === 'OK') {
// 上传完成
fileItem.status = 'done'
fileItem.message = '上传成功'
}
else {
// 后端返回非 OK
throw new Error(saveRes.data || '上传失败')
}
fileList.value = [...fileList.value]
loadFileData()
}
catch (err: any) {
console.error('上传失败:', err)
fileItem.status = 'failed'
fileItem.message = err.message || '上传失败'
fileList.value = [...fileList.value]
}
}
function beforeRemove(index: number, fileItem: UploadFileItem): boolean | Promise<void> {
return new Promise((resolve, reject) => {
dialog.confirm({
title: '提示',
message: `确定删除该张图片吗?`,
onConfirm: () => resolve(),
onCancel: () => reject(),
onClose: () => reject(),
})
})
}
function onRemove(index: number, item: UploadFileItem) {
delSubRecordApi({
module: 'bf_files',
id: item.subid,
bucketName: aliyunBucket,
}).then((res) => {
if (res.data.result) {
toast.success('删除成功')
}
})
}
</script>
<template>
<view class="file">
<sar-result v-if="fileList.length === 0 && !isEdit" status="info" title="提示" description="暂无附件" />
<sar-upload v-model="fileList" :readonly="!isEdit" :after-read="afterRead" :before-read="beforeRead" :before-remove="beforeRemove" multiple @item-click="onItemClick" @remove="onRemove" />
</view>
</template>
<style lang="scss" scoped>
.file {
padding: 36rpx;
}
</style>
@@ -0,0 +1,253 @@
<script lang="ts" setup>
import type { FieldValidateError, FormExpose, FormRules } from 'sard-uniapp'
import { getWindowInfo, toast } from 'sard-uniapp'
import { reactive, ref } from 'vue'
import { loaddataApi, loginApi } from '@/request/api'
import { useUserStore } from '@/store'
const userStore = useUserStore()
const startOffset = ref(getWindowInfo().statusBarHeight + 60)
const _loading = ref(false)
const formModel = reactive({
orgid: userStore.userInfo.orgid,
userid: userStore.userInfo.userid,
password: '',
})
const formRules: FormRules = {
orgid: {
required: true,
message: '请输入机构码',
},
userid: {
required: true,
message: '请输入账号',
},
}
const formRef = ref<FormExpose>()
async function onSubmit() {
try {
await formRef.value?.validate()
_loading.value = true
const res = await loginApi({
orgid: formModel.orgid,
userid: formModel.userid,
password: formModel.password,
})
if (!res?.data) {
toast.fail('登录失败,请检查账号密码')
return
}
userStore.userInfo = {
...userStore.userInfo,
orgid: formModel.orgid,
userid: formModel.userid,
token: res.data,
}
let username = ''
try {
const userRes = await loaddataApi({
viewname: 'v_b_truck',
soption: '',
orderfield: '',
searchcondition: `b_id = '${formModel.userid}'`,
})
if (userRes?.data?.length > 0) {
username = userRes.data[0].v_worker_name || ''
}
}
catch (err) {
console.warn('获取用户信息失败', err)
}
userStore.userInfo = {
...userStore.userInfo,
username,
}
uni.showToast({
title: '登录成功',
icon: 'success',
})
uni.switchTab({
url: '/pages/home/index',
})
}
catch (err: any) {
// 表单验证错误
if (Array.isArray(err)) {
toast.fail(err[0].message)
}
else {
console.error(err)
toast.fail(err.message || '登录异常')
}
}
finally {
_loading.value = false
}
}
</script>
<template>
<view class="form-container">
<sar-form-plain
ref="formRef"
:model="formModel"
:rules="formRules"
scroll-to-first-error
:scroll-into-view-options="{
startOffset,
position: 'start',
}"
>
<view class="form-description">
<view class="form-title">
欢迎登录
</view>
<view class="form-subtitle">
请输入机构码、账号和密码
</view>
</view>
<view class="form-list">
<sar-form-item-plain name="orgid">
<view class="form-label">
<text>机构码</text>
</view>
<view class="form-input">
<sar-input
v-model="formModel.orgid"
placeholder="请输入机构码"
clearable
>
<template #prepend>
<sar-icon family="iconfont" name="company" size="36rpx" />
</template>
</sar-input>
</view>
</sar-form-item-plain>
<sar-form-item-plain name="userid">
<view class="form-label">
账号
</view>
<view class="form-input">
<sar-input
v-model="formModel.userid"
placeholder="请输入账号"
clearable
>
<template #prepend>
<sar-icon family="iconfont" name="user" size="36rpx" />
</template>
</sar-input>
</view>
</sar-form-item-plain>
<sar-form-item-plain name="password">
<view class="form-label">
密码
</view>
<view class="form-input">
<sar-input
v-model="formModel.password"
placeholder="请输入密码"
type="password"
clearable
show-eye
>
<template #prepend>
<sar-icon family="iconfont" name="lock" size="36rpx" />
</template>
</sar-input>
</view>
</sar-form-item-plain>
</view>
<view class="footer">
<sar-button loading-type="clock" :loading="_loading" @click="onSubmit">
登录
</sar-button>
</view>
</sar-form-plain>
</view>
</template>
<style>
page {
background-color: #fff;
}
</style>
<style lang="scss" scoped>
@import '@/style/mixins.scss';
.form-container {
padding: 0rpx 32rpx;
}
.form-description {
padding: 64rpx 0 64rpx;
color: var(--sar-secondary-color);
.form-title {
font-size: 64rpx;
font-weight: bold;
color: #000;
}
.form-subtitle {
margin-top: 16rpx;
font-size: 32rpx;
}
}
.form-list {
display: flex;
flex-direction: column;
@include flexGap(36rpx, column);
}
.form-label {
display: flex;
font-size: 32rpx;
}
.form-star {
color: var(--sar-danger);
}
.form-input {
margin-top: 20rpx;
:deep(.sar-input) {
background-color: #fff;
.sar-icon {
color: var(--sar-input-placeholder-color);
}
}
:deep(.sar-input_focused) {
.sar-input__prepend {
.sar-icon {
color: var(--sar-input-focused-border-color);
}
}
}
}
.footer {
// position: sticky;
// bottom: 0;
padding-top: 64rpx;
:deep(.sar-button) {
font-size: 32rpx;
}
}
</style>
@@ -0,0 +1,253 @@
<script lang="ts" setup>
import type { FieldValidateError, FormExpose, FormRules } from 'sard-uniapp'
import { getWindowInfo, toast } from 'sard-uniapp'
import { reactive, ref } from 'vue'
import { loaddataApi, saveobjtApi } from '@/request/api'
import { useUserStore } from '@/store'
const userStore = useUserStore()
const startOffset = ref(getWindowInfo().statusBarHeight + 60)
const submitLoading = ref(false)
const formModel = reactive({
oldPassword: '',
newPassword: '',
confirmPassword: '',
})
const formRules: FormRules = {
}
const formRef = ref<FormExpose>()
async function onSubmit() {
try {
await formRef.value?.validate()
if (formModel.confirmPassword !== formModel.newPassword) {
toast.fail('两次输入的密码不一致,请重新输入')
return
}
submitLoading.value = true
const searchcondition = `b_id = '${userStore.userInfo.userid}' AND ISNULL(b_password, '') = ISNULL('${formModel.oldPassword}', '')`
const searchRes = await loaddataApi({
viewname: 'v_mini_loginid',
searchcondition: `${searchcondition}`,
orderfield: 'b_id',
soption: '',
})
if (!searchRes.data || searchRes.data.length === 0) {
toast.fail('旧密码错误,请重新输入')
return
}
const updateObj = {
b_id: userStore.userInfo.userid,
b_password: formModel.newPassword,
}
const tables = []
const insertValues = []
const updateValues = []
const delValues = []
const keys = []
tables.push('b_truck')
insertValues.push('[]')
updateValues.push(JSON.stringify([updateObj]))
delValues.push('[]')
keys.push('b_id')
const saveobjtRes = await saveobjtApi({
tables,
insertValues,
delValues,
updateValues,
keys,
})
if (saveobjtRes.data === 'OK') {
toast.success('密码修改成功,请重新登录')
setTimeout(() => {
uni.navigateTo({
url: '/pages-sub/login/index',
})
}, 500)
}
else {
console.log(saveobjtRes)
toast.fail(saveobjtRes.data || '修改失败')
}
}
catch (error: any) {
// 表单校验错误
if (Array.isArray(error) && error[0]?.message) {
toast.fail(error[0].message)
}
else {
toast.fail('操作失败,请重试')
console.error(error)
}
}
finally {
// 关闭 loading
submitLoading.value = false
}
}
</script>
<template>
<view class="form-container">
<sar-form-plain
ref="formRef"
:model="formModel"
:rules="formRules"
scroll-to-first-error
:scroll-into-view-options="{
startOffset,
position: 'start',
}"
>
<view class="form-description">
<view class="form-title">
密码修改
</view>
<view class="form-subtitle">
确保账户安全,请谨慎操作
</view>
</view>
<view class="form-list">
<sar-form-item-plain name="password">
<view class="form-label">
旧密码
</view>
<view class="form-input">
<sar-input
v-model="formModel.oldPassword"
placeholder="请输入旧密码"
type="password"
show-eye
>
<template #prepend>
<sar-icon family="iconfont" name="lock" size="36rpx" />
</template>
</sar-input>
</view>
</sar-form-item-plain>
<sar-form-item-plain name="password">
<view class="form-label">
新密码
</view>
<view class="form-input">
<sar-input
v-model="formModel.newPassword"
placeholder="请输入新密码"
type="password"
show-eye
>
<template #prepend>
<sar-icon family="iconfont" name="lock" size="36rpx" />
</template>
</sar-input>
</view>
</sar-form-item-plain>
<sar-form-item-plain name="password">
<view class="form-label">
确认密码
</view>
<view class="form-input">
<sar-input
v-model="formModel.confirmPassword"
placeholder="请输入确认密码"
type="password"
show-eye
>
<template #prepend>
<sar-icon family="iconfont" name="lock" size="36rpx" />
</template>
</sar-input>
</view>
</sar-form-item-plain>
</view>
<view class="footer">
<sar-button loading-type="clock" :loading="submitLoading" @click="onSubmit">
确认修改
</sar-button>
</view>
</sar-form-plain>
</view>
</template>
<style>
page {
background-color: #fff;
}
</style>
<style lang="scss" scoped>
@import '@/style/mixins.scss';
.form-container {
padding: 0rpx 32rpx;
}
.form-description {
padding: 64rpx 0 64rpx;
color: var(--sar-secondary-color);
.form-title {
font-size: 64rpx;
font-weight: bold;
color: #000;
}
.form-subtitle {
margin-top: 16rpx;
font-size: 32rpx;
}
}
.form-list {
display: flex;
flex-direction: column;
@include flexGap(36rpx, column);
}
.form-label {
display: flex;
font-size: 32rpx;
}
.form-star {
color: var(--sar-danger);
}
.form-input {
margin-top: 20rpx;
:deep(.sar-input) {
background-color: #fff;
.sar-icon {
color: var(--sar-input-placeholder-color);
}
}
:deep(.sar-input_focused) {
.sar-input__prepend {
.sar-icon {
color: var(--sar-input-focused-border-color);
}
}
}
}
.footer {
// position: sticky;
// bottom: 0;
padding-top: 64rpx;
}
</style>