增加app模板和代码生成

This commit is contained in:
xiangheng
2024-06-04 00:41:55 +08:00
parent 3ea0b9ad5d
commit 89eb9ccc72
916 changed files with 97550 additions and 9 deletions

View File

@@ -8,6 +8,9 @@
}, },
{ {
"path": "docs" "path": "docs"
},
{
"path": "x_admin_app"
} }
] ]
} }

View File

@@ -42,12 +42,14 @@
<uv-back-top :scroll-top="scrollTop"></uv-back-top> <uv-back-top :scroll-top="scrollTop"></uv-back-top>
<uv-empty v-if="pager.loading =='nomore'&&pager.lists.length == 0" marginTop="150" mode="data"></uv-empty>
<uv-loading-page <uv-loading-page
:loading="pager.pageNo == 1 && pager.loading == 'loading'" :loading="pager.pageNo == 1 && pager.loading == 'loading'"
loading-text="加载中..." loading-text="加载中..."
font-size="24rpx" font-size="24rpx"
></uv-loading-page> ></uv-loading-page>
<uv-load-more <uv-load-more
v-if="pager.lists.length > 0"
:status="pager.loading" :status="pager.loading"
:loading-text="pager.loadingText" :loading-text="pager.loadingText"
:loadmore-text="pager.loadmoreText" :loadmore-text="pager.loadmoreText"

View File

@@ -0,0 +1,34 @@
// 请将pages里的数据手动合并到根目录下pages.json中
{
"pages": [
{
"path": "pages/{{{ .ModuleName }}}/index",
"style": {
"navigationBarTitleText": "{{{.FunctionName}}}",
"enablePullDownRefresh": true,
"onReachBottomDistance": 100,
"navigationStyle": "custom"
}
},
{
"path": "pages/{{{ .ModuleName }}}/details",
"style": {
"navigationBarTitleText": "{{{.FunctionName}}}详情",
"enablePullDownRefresh": true,
"navigationStyle": "custom"
}
},
{
"path": "pages/{{{ .ModuleName }}}/edit",
"style": {
"navigationBarTitleText": "编辑{{{.FunctionName}}}"
}
},
{
"path": "pages/{{{ .ModuleName }}}/search",
"style": {
"navigationBarTitleText": "搜索{{{.FunctionName}}}"
}
}
]
}

View File

@@ -8,7 +8,9 @@
<x-date-range v-model:startTime="form.{{{ (toCamelCase .GoField) }}}Start" <x-date-range v-model:startTime="form.{{{ (toCamelCase .GoField) }}}Start"
v-model:endTime="form.{{{ (toCamelCase .GoField) }}}End"></x-date-range> v-model:endTime="form.{{{ (toCamelCase .GoField) }}}End"></x-date-range>
{{{- else if or (eq .HtmlType "select") (eq .HtmlType "radio") }}} {{{- else if or (eq .HtmlType "select") (eq .HtmlType "radio") }}}
{{{- if ne .DictType "" }}}
<x-picker v-model="form.{{{ (toCamelCase .GoField) }}}" valueKey="value" labelKey="name" :columns="dictData.{{{ .DictType }}}"></x-picker> <x-picker v-model="form.{{{ (toCamelCase .GoField) }}}" valueKey="value" labelKey="name" :columns="dictData.{{{ .DictType }}}"></x-picker>
{{{- end }}}
{{{- else if eq .HtmlType "input" }}} {{{- else if eq .HtmlType "input" }}}
<uv-input v-model="form.{{{ (toCamelCase .GoField) }}}"> </uv-input> <uv-input v-model="form.{{{ (toCamelCase .GoField) }}}"> </uv-input>
{{{- end }}} {{{- end }}}
@@ -68,7 +70,7 @@ const { dictData } = useDictData([{{{- range .DictFields }}}'{{{ . }}}'{{{- if n
return toast("请输入查询条件"); return toast("请输入查询条件");
} }
toPath("/pages/equipment/equipment", search); toPath("/pages/{{{ .ModuleName }}}/index", search);
} }
</script> </script>

View File

@@ -170,6 +170,7 @@ func (tu templateUtil) GetTemplatePaths(genTpl string) []string {
"uniapp/index.vue.tpl", "uniapp/index.vue.tpl",
"uniapp/details.vue.tpl", "uniapp/details.vue.tpl",
"uniapp/search.vue.tpl", "uniapp/search.vue.tpl",
"uniapp/pages.json.tpl",
} }
if genTpl == GenConstants.TplCrud { if genTpl == GenConstants.TplCrud {
tplPaths = append(tplPaths, "vue/index.vue.tpl") tplPaths = append(tplPaths, "vue/index.vue.tpl")
@@ -223,11 +224,12 @@ func (tu templateUtil) GetFilePaths(tplCodeMap map[string]string, ModuleName str
"vue/index.vue.tpl": strings.Join([]string{"admin/src/views/", ModuleName, "/index.vue"}, ""), // "admin/src/views/%s/index.vue", "vue/index.vue.tpl": strings.Join([]string{"admin/src/views/", ModuleName, "/index.vue"}, ""), // "admin/src/views/%s/index.vue",
"vue/index-tree.vue.tpl": strings.Join([]string{"admin/src/views/", ModuleName, "/index-tree.vue"}, ""), // "admin/src/views/%s/index-tree.vue", "vue/index-tree.vue.tpl": strings.Join([]string{"admin/src/views/", ModuleName, "/index-tree.vue"}, ""), // "admin/src/views/%s/index-tree.vue",
"uniapp/api.ts.tpl": strings.Join([]string{"uniapp/api/", ModuleName, ".ts"}, ""), "uniapp/api.ts.tpl": strings.Join([]string{"x_admin_app/api/", ModuleName, ".ts"}, ""),
"uniapp/edit.vue.tpl": strings.Join([]string{"uniapp/pages/", ModuleName, "/edit.vue"}, ""), "uniapp/edit.vue.tpl": strings.Join([]string{"x_admin_app/pages/", ModuleName, "/edit.vue"}, ""),
"uniapp/index.vue.tpl": strings.Join([]string{"uniapp/pages/", ModuleName, "/index.vue"}, ""), "uniapp/index.vue.tpl": strings.Join([]string{"x_admin_app/pages/", ModuleName, "/index.vue"}, ""),
"uniapp/search.vue.tpl": strings.Join([]string{"uniapp/pages/", ModuleName, "/search.vue"}, ""), "uniapp/search.vue.tpl": strings.Join([]string{"x_admin_app/pages/", ModuleName, "/search.vue"}, ""),
"uniapp/details.vue.tpl": strings.Join([]string{"uniapp/pages/", ModuleName, "/details.vue"}, ""), "uniapp/details.vue.tpl": strings.Join([]string{"x_admin_app/pages/", ModuleName, "/details.vue"}, ""),
"uniapp/pages.json.tpl": strings.Join([]string{"x_admin_app/pages/", ModuleName, "/pages.json"}, ""),
} }
filePath := make(map[string]string) filePath := make(map[string]string)
for tplPath, tplCode := range tplCodeMap { for tplPath, tplCode := range tplCodeMap {

View File

@@ -4,9 +4,13 @@ import "x_admin/core"
//MonitorProjectListReq 错误项目列表参数 //MonitorProjectListReq 错误项目列表参数
type MonitorProjectListReq struct { type MonitorProjectListReq struct {
ProjectKey string `form:"projectKey"` // 项目uuid ProjectKey string `form:"projectKey"` // 项目uuid
ProjectName string `form:"projectName"` // 项目名称 ProjectName string `form:"projectName"` // 项目名称
ProjectType string `form:"projectType"` // 项目类型go java web node php 等 ProjectType string `form:"projectType"` // 项目类型go java web node php 等
CreateTimeStart string `form:"createTimeStart"` // 开始创建时间
CreateTimeEnd string `form:"createTimeEnd"` // 结束创建时间
UpdateTimeStart string `form:"updateTimeStart"` // 开始更新时间
UpdateTimeEnd string `form:"updateTimeEnd"` // 结束更新时间
} }
//MonitorProjectDetailReq 错误项目详情参数 //MonitorProjectDetailReq 错误项目详情参数

View File

@@ -49,6 +49,18 @@ func (service monitorProjectService) List(page request.PageReq, listReq MonitorP
if listReq.ProjectType != "" { if listReq.ProjectType != "" {
dbModel = dbModel.Where("project_type = ?", listReq.ProjectType) dbModel = dbModel.Where("project_type = ?", listReq.ProjectType)
} }
if listReq.CreateTimeStart != "" {
dbModel = dbModel.Where("create_time >= ?", listReq.CreateTimeStart)
}
if listReq.CreateTimeEnd != "" {
dbModel = dbModel.Where("create_time <= ?", listReq.CreateTimeEnd)
}
if listReq.UpdateTimeStart != "" {
dbModel = dbModel.Where("update_time >= ?", listReq.UpdateTimeStart)
}
if listReq.UpdateTimeEnd != "" {
dbModel = dbModel.Where("update_time <= ?", listReq.UpdateTimeEnd)
}
dbModel = dbModel.Where("is_delete = ?", 0) dbModel = dbModel.Where("is_delete = ?", 0)
// 总数 // 总数
var count int64 var count int64

25
x_admin_app/.gitignore vendored Normal file
View File

@@ -0,0 +1,25 @@
.DS_Store
node_modules/
dist/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
package-lock.json
tests/**/coverage/
pnpm-lock.yaml
# Editor directories and files
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
stats.html
.vscode/*
!/.vscode/settings.json
release
dist-electron
unpackage/*
!unpackage/res
HBuilderConfigTemp.json

View File

@@ -0,0 +1,20 @@
{
"version" : "1.0",
"configurations" : [
{
"playground" : "custom",
"type" : "uni-app:app-android"
},
{
"app-plus" :
{
"launchtype" : "local"
},
"mp-weixin" :
{
"launchtype" : "local"
},
"type" : "uniCloud"
}
]
}

11
x_admin_app/.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,11 @@
{
"cSpell.words": [
"dcloudio",
"fota",
"IMEI",
"loadmore",
"nomore",
"pinia",
"realname"
]
}

62
x_admin_app/App.vue Normal file
View File

@@ -0,0 +1,62 @@
<script>
import {
useUserStore
} from "@/stores/user";
export default {
onLaunch: function() {
const userStore = useUserStore();
userStore
.getInfo()
.then((res) => {
console.log("userInfo", res);
})
.catch((err) => {
uni.redirectTo({
url: "/pages/login/login",
});
});
},
onShow: function() {},
onHide: function() {},
};
</script>
<style>
#app {
/* 解决app上uv-sticky浮动的问题 ,估计又修复了先注释掉*/
/* height: auto; */
}
button:after {
border: 0 !important;
}
/*每个页面公共css */
.h100 {
height: 100%;
}
.row {
display: flex;
flex-direction: row;
}
.flex_1 {
flex: 1;
}
.center {
justify-content: center;
align-items: center;
}
.right {
display: flex;
flex-direction: row;
justify-content: flex-end;
}
.search {
padding: 5rpx;
background-color: #fff;
}
</style>

View File

@@ -0,0 +1,52 @@
{
//项目名字或项目绝对路径
"project": "x_admin_app",
//打包平台 默认值android 值有"android","ios" 如果要打多个逗号隔开打包平台
"platform": "android",
//是否使用自定义基座 默认值false true自定义基座 false自定义证书
"iscustom": false,
//打包方式是否为安心打包默认值false,true安心打包,false传统打包
"safemode": true,
//android打包参数
"android": {
//安卓包名
"packagename": "uni.UNIFB29F21",
//安卓打包类型 默认值0 0 使用自有证书 1 使用公共证书 2 使用老版证书
"androidpacktype": "3",
//安卓使用自有证书自有打包证书参数
//安卓打包证书别名,自有证书打包填写的参数
"certalias": "",
//安卓打包证书文件路径,自有证书打包填写的参数
"certfile": "",
//安卓打包证书密码,自有证书打包填写的参数
"certpassword": "",
//安卓平台要打的渠道包 取值有"google","yyb","360","huawei","xiaomi","oppo","vivo",如果要打多个逗号隔开
"channels": ""
},
//ios打包参数
"ios": {
//ios appid
"bundle": "uni.UNIFB29F21",
//ios打包支持的设备类型 默认值iPhone 值有"iPhone","iPad" 如果要打多个逗号隔开打包平台
"supporteddevice": "iPhone,iPad",
//iOS打包是否打越狱包,只有值为true时打越狱包,false打正式包
"isprisonbreak": false,
//iOS使用自定义证书打包的profile文件路径
"profile": "",
//iOS使用自定义证书打包的p12文件路径
"certfile": "",
//iOS使用自定义证书打包的证书密码
"certpassword": ""
},
//是否混淆 true混淆 false关闭
"isconfusion": false,
//开屏广告 true打开 false关闭
"splashads": false,
//悬浮红包广告true打开 false关闭
"rpads": false,
//push广告 true打开 false关闭
"pushads": false,
//加入换量联盟 true加入 false不加入
"exchange": false
}

View File

@@ -0,0 +1,3 @@
{
"prompt" : "template"
}

View File

@@ -0,0 +1,57 @@
import { request } from '@/utils/request'
// 监控项目列表
export function monitor_project_list(params?: Record<string, any>) {
return request({
url: '/monitor_project/list',
method: 'GET',
data: params
})
}
// 监控项目列表-所有
export function monitor_project_list_all(params?: Record<string, any>) {
return request({
url: '/monitor_project/listAll',
method: 'GET',
data: params
})
}
// 监控项目详情
export function monitor_project_detail(id: number | string) {
return request({
url: '/monitor_project/detail',
method: 'GET',
data: { id }
})
}
// 监控项目新增
export function monitor_project_add(data: Record<string, any>) {
return request({
url: '/monitor_project/add',
method: "POST",
data,
});
}
// 监控项目编辑
export function monitor_project_edit(data: Record<string, any>) {
return request({
url: '/monitor_project/edit',
method: "POST",
data,
});
}
// 监控项目删除
export function monitor_project_delete(id: number | string) {
return request({
url: '/monitor_project/del',
method: "POST",
data:{
id
},
});
}

29
x_admin_app/api/user.js Normal file
View File

@@ -0,0 +1,29 @@
import {request} from '@/utils/request';
export function login(data) {
return request({
url: '/system/login',
method: 'POST',
data
})
}
export function getInfo(token) {
return request({
url: '/system/admin/self',
method: 'GET',
data: {
token
}
})
}
export function logout() {
return request({
url: '/system/logout',
method: 'GET'
})
}

View File

@@ -0,0 +1,39 @@
<template>
<uv-load-more
:status="props.status"
:loading-text="props.loadingText"
:loadmore-text="props.loadmoreText"
:nomore-text="props.nomoreText"
@loadmore="loadmore"
/>
</template>
<script setup>
import { onReady, onPullDownRefresh, onReachBottom } from "@dcloudio/uni-app";
import { ref, defineEmits,defineModel } from "vue";
var pager = defineModel();
const emits = defineEmits(["loadmore"]);
const props = defineProps({
status: {
type: String,
default: "loadmore",
},
loadingText: {
type: String,
default: "努力加载中",
},
loadmoreText: {
type: String,
default: "轻轻上拉",
},
nomoreText: {
type: String,
default: "实在没有了",
},
});
function loadmore(e) {
emits("loadmore", e);
}
</script>

View File

@@ -0,0 +1,11 @@
import CryptoJS from 'crypto-js'
/**
* @word 要加密的内容
* @keyWord String 服务器随机返回的关键字
* */
export function aesEncrypt(word,keyWord="XwKsGlMcdPMEhR1B"){
var key = CryptoJS.enc.Utf8.parse(keyWord);
var srcs = CryptoJS.enc.Utf8.parse(word);
var encrypted = CryptoJS.AES.encrypt(srcs, key, {mode:CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7});
return encrypted.toString();
}

View File

@@ -0,0 +1,20 @@
// let baseUrl = "https://captcha.anji-plus.com/captcha-api"
import env from "@/utils/env";
let baseUrl =env.baseUrl+'/api/common';
export const myRequest = (option={})=>{
return new Promise((reslove,reject)=>{
uni.request({
url: baseUrl + option.url,
data :option.data,
method:option.method || "GET",
success: (result) => {
reslove(result)
},
fail:(error)=>{
reject(error)
}
})
})
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,114 @@
<template>
<view class="x-card" :style="{ marginBottom: bottom + 'px' }">
<view class="x-card-image" v-if="icon">
<uv-image width="100%" :src="icon" mode="widthFix"></uv-image>
</view>
<view class="x-card-title" v-if="title">
<text>{{ title }}</text>
</view>
<view class="x-card-content" v-if="content">
<slot name="contentSlot">
<text>{{ content }}</text>
</slot>
</view>
<view class="x-card-bottom" v-if="btnText">
<slot name="btnSlot">
<button :open-type="openType" @click="btnClick">{{ btnText }}</button>
</slot>
</view>
</view>
</template>
<script>
export default {
props: {
icon: {
default: "",
type: [String, Number],
},
title: {
default: "",
type: [String, Number],
},
content: {
default: "",
type: [String, Number],
},
btnText: {
default: "",
type: [String, Number],
},
openType: {
default: "",
type: String,
},
bottom: {
default: "0",
type: [String, Number],
},
url: {
default: "",
type: String,
},
},
data() {
return {};
},
computed: {},
methods: {
btnClick() {
if (this.url) {
this.$toPath(this.url, {
// id: item.id
});
return;
}
this.$emit("btnClick");
},
},
};
</script>
<style lang="scss">
.x-card {
background-color: white;
margin:0 10rpx;
padding: 10px 10px;
border-radius: 8px;
// margin-bottom: 10px;
.x-card-image {
padding-bottom: 10px;
}
.x-card-title {
padding-bottom: 10rpx;
text {
line-height: 1;
font-size: 32rpx;
// color: #aaaaaa;
}
}
.x-card-content {
padding-bottom: 10rpx;
text {
line-height: 1;
font-size: 26rpx;
// color: #aaaaaa;
}
}
.x-card-bottom {
padding-top: 20rpx;
button {
margin: 0;
padding: 16rpx 16rpx;
line-height: 1;
border: 0;
font-size: 24rpx;
background-color: rgba(111, 148, 205, 1);
color: white;
}
}
}
</style>

View File

@@ -0,0 +1,46 @@
<template>
<uv-input
:modelValue="title"
:readonly="true"
placeholder="请选择时间"
@click="calendarsRef.open()"
>
</uv-input>
<uv-calendars
ref="calendarsRef"
mode="range"
:date="[props.startTime, props.endTime]"
@confirm="Confirm"
/>
</template>
<script setup>
import { ref, computed } from "vue";
const emit = defineEmits(["update:startTime", "update:endTime"]);
const props = defineProps({
startTime: {
type: String,
default: "",
},
endTime: {
type: String,
default: "",
},
});
let calendarsRef = ref(null);
function Confirm(e) {
console.log(e);
emit("update:startTime", e.range.before);
emit("update:endTime", e.range.after);
}
const title = computed(() => {
if (!props.startTime || !props.endTime) {
return "";
}
return props.startTime + "-" + props.endTime;
});
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,106 @@
<template >
<view class="x-list" :style="{marginBottom:bottom+'px'}">
<view class="x-list-left" v-if="icon">
<uv-image width="24px" height="24px" :src="icon" mode="widthFix"></uv-image>
</view>
<view class="x-list-content">
<view v-if="title">{{title}}</view>
<text v-if="des">{{des}}</text>
</view>
<view class="x-list-right" v-if="btnText">
<slot name="right">
<button :open-type="openType" @click="btnClick">{{btnText}}</button>
</slot>
</view>
</view>
</template>
<script>
export default {
props:{
icon:{
default:"",
type:String
},
title:{
default:"",
type:String
},
des:{
default:"",
type:String
},
btnText:{
default:"",
type:String
},
openType:{
default:"",
type:String
},
bottom:{
default:"0",
type:[String,Number]
},
url:{
default:"",
type:String
},
},
data() {
return {
}
},
computed:{
},
methods: {
btnClick(){
if(this.url){
this.$toPath(this.url, {
// id: item.id
})
return
}
this.$emit('btnClick')
},
}
}
</script>
<style lang="scss">
.x-list {
display: flex;
// justify-content: center;
align-items: center;
background-color: white;
padding: 10px 10px;
border-radius: 8px;
// margin-bottom: 10px;
.x-list-content {
font-size: 16px;
flex: 1;
padding: 0 10px;
text {
line-height: 1;
font-size: 13px;
color: #AAAAAA;
}
}
.x-list-right {
button {
margin: 0;
padding: 6px 8px;
line-height: 1;
border: 0;
font-size: 12px;
background-color: rgba(111, 148, 205, 1);
color: white;
}
}
}
</style>

View File

@@ -0,0 +1,108 @@
<template>
<uv-input
:modelValue="selectItem?.[props.labelKey]"
placeholder="请选择"
readonly
@click="openPicker"
>
</uv-input>
<uv-picker
ref="picker"
:columns="columns"
:keyName="props.labelKey"
:defaultIndex="pickerIndex"
@confirm="handleConfirm"
></uv-picker>
</template>
<script setup>
import {
reactive,
ref,
defineModel,
onMounted,
computed,
watch,
} from "vue";
const props = defineProps({
columns: {
type: Array,
default: () => [],
},
labelKey: {
type: String,
default: "label",
},
valueKey: {
type: String,
default: "id",
},
});
const columns = computed(() => {
return [props.columns];
});
const picker = ref(null);
const model = defineModel();
const pickerIndex = ref([0]);
const selectItem = ref({});
function openPicker() {
picker.value.open();
}
function handleConfirm(e) {
// debugger;
if (e.value[0]!==undefined) {
model.value = e.value[0][props.valueKey];
selectItem.value = e.value[0];
}else{
model.value = null;
selectItem.value = {};
console.log('handleConfirm没有数据',e);
}
}
function updateSelectItem() {
console.log("updateSelectItem", model.value, props.columns);
if (!model.value) {
pickerIndex.value = [0];
selectItem.value = {};
return;
}
if(props.columns.length==0){
pickerIndex.value = [0];
selectItem.value = {};
return;
}
let find=false
for (let index = 0; index < props.columns.length; index++) {
const item = props.columns[index];
if (model.value == item[props.valueKey]) {
selectItem.value = item;
pickerIndex.value = [index];
find=true
break;
}
}
if(!find){
selectItem.value = {};
pickerIndex.value = [0];
model.value = null;
}
}
onMounted(() => {
console.log("onMounted");
updateSelectItem();
});
watch(
() => [model.value, props.columns],
(newVal) => {
console.log("watch", newVal);
updateSelectItem();
}
);
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,55 @@
import { request } from "@/utils/request";
import { reactive, toRaw } from "vue";
interface Options {
[propName: string]: string
}
export function useDictOptions<T = any>(options: Options) {
const optionsData: any = reactive({});
const optionsKey = Object.keys(options);
const apiLists = optionsKey.map((key) => {
const path = options[key];
optionsData[key] = [];
return () =>request({
url: path,
method: "GET",
});
});
const refresh = async () => {
const res = await Promise.allSettled<Promise<any>>(
apiLists.map((api) => api())
);
console.log(res);
res.forEach((item, index) => {
const key = optionsKey[index];
if (item.status == "fulfilled") {
const data = item.value;
optionsData[key] = data.data;
}
});
};
refresh();
return {
optionsData: optionsData as T,
refresh,
};
}
export function useDictData<T = any>(dict: string[]) {
const options: Options = {};
for (const type of dict) {
options[type] = `/setting/dict/data/all?dictType=${type}`
}
const { optionsData } = useDictOptions<T>(options);
console.log('optionsData',optionsData);
return {
dictData: optionsData as T,
};
}
// export function useAllList<T = any>(options: Options) {}

View File

@@ -0,0 +1,106 @@
import { reactive, toRaw } from "vue";
import { toast } from "@/utils/utils";
// 分页钩子函数
interface Options {
pageNo?: number;
pageSize?: number;
loadingText?: string;
loadmoreText?: string;
nomoreText?: string;
fetchFun: (_arg: any) => Promise<any>;
params?: Record<any, any>;
firstStatus?: string; //loadmore - 加载前loading - 加载中nomore - 没有数据
}
export function usePaging(options: Options) {
const {
pageNo = 1,
pageSize = 30,
loadingText = "努力加载中",
loadmoreText = "继续加载",
nomoreText = "没有数据了",
fetchFun,
params = {},
firstStatus = "loadmore",
} = options;
// 记录分页初始参数
const paramsInit: Record<any, any> = Object.assign({}, toRaw(params));
// 分页数据
const pager = reactive({
pageNo,
pageSize,
loadingText,
loadmoreText,
nomoreText,
loading: firstStatus,
count: 0,
lists: [] as any[],
});
// 请求分页接口
const getLists = () => {
pager.loading = "loading";
return fetchFun({
pageNo: pager.pageNo,
pageSize: pager.pageSize,
...params,
})
.then((res: any) => {
uni.stopPullDownRefresh();
if (res.code == 200) {
pager.count = res?.data?.count;
var list = res?.data?.lists || [];
pager.lists.push(...list);
if (res?.data?.lists?.length < pager.pageSize) {
pager.loading = "nomore";
} else {
pager.loading = "loadmore";
}
}else{
pager.loading = "loadmore";
}
return Promise.resolve(res);
})
.catch((err: any) => {
uni.stopPullDownRefresh();
toast(err.message);
pager.loading = "nomore";
return Promise.reject(err);
});
};
// 下一页
const NextPage = () => {
if (pager.loading === "nomore") {
toast("没有数据了");
return;
}
pager.pageNo += 1;
getLists();
};
// 重置为第一页
const resetPage = () => {
pager.pageNo = 1;
pager.lists = [];
getLists();
};
// 重置参数
const resetParams = () => {
Object.keys(paramsInit).forEach((item) => {
params[item] = paramsInit[item];
});
pager.lists = [];
getLists();
};
return {
pager,
getLists,
NextPage,
resetParams,
resetPage,
};
}

7
x_admin_app/images.d.ts vendored Normal file
View File

@@ -0,0 +1,7 @@
declare module '*.svg'
declare module '*.png'
declare module '*.jpg'
declare module '*.jpeg'
declare module '*.gif'
declare module '*.bmp'
declare module '*.tiff'

20
x_admin_app/index.html Normal file
View File

@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8" />
<script>
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
CSS.supports('top: constant(a)'))
document.write(
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
</script>
<title></title>
<!--preload-links-->
<!--app-context-->
</head>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/main.js"></script>
</body>
</html>

19
x_admin_app/main.js Normal file
View File

@@ -0,0 +1,19 @@
import App from "./App";
import { createSSRApp } from "vue";
import { installMethods } from "@/methods/index";
import * as Pinia from "pinia";
export function createApp() {
const app = createSSRApp(App);
app.use(Pinia.createPinia());
installMethods(app);
return {
app,
Pinia, // 此处必须将 Pinia 返回
};
}

132
x_admin_app/manifest.json Normal file
View File

@@ -0,0 +1,132 @@
{
"name": "x_admin_app",
"appid": "__UNI__FB29F21",
"description": "",
"versionName": "1.0.2",
"versionCode": 102,
"transformPx": false,
"app-plus": {
"compatible": {
"ignoreVersion": true
},
"usingComponents": true,
"nvueStyleCompiler": "uni-app",
"compilerVersion": 3,
"splashscreen": {
"alwaysShowBeforeRender": true,
"waiting": true,
"autoclose": true,
"delay": 0
},
"modules": {},
"distribute": {
"android": {
"permissions": [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
],
"minSdkVersion": 26,
"schemes": "x_admin",
"targetSdkVersion": 30
},
"ios": {
"urltypes": "x_admin",
"dSYMs": false
},
"sdkConfigs": {
"ad": {}
},
"icons": {
"android": {
"hdpi": "unpackage/res/icons/72x72.png",
"xhdpi": "unpackage/res/icons/96x96.png",
"xxhdpi": "unpackage/res/icons/144x144.png",
"xxxhdpi": "unpackage/res/icons/192x192.png"
},
"ios": {
"appstore": "unpackage/res/icons/1024x1024.png",
"ipad": {
"app": "unpackage/res/icons/76x76.png",
"app@2x": "unpackage/res/icons/152x152.png",
"notification": "unpackage/res/icons/20x20.png",
"notification@2x": "unpackage/res/icons/40x40.png",
"proapp@2x": "unpackage/res/icons/167x167.png",
"settings": "unpackage/res/icons/29x29.png",
"settings@2x": "unpackage/res/icons/58x58.png",
"spotlight": "unpackage/res/icons/40x40.png",
"spotlight@2x": "unpackage/res/icons/80x80.png"
},
"iphone": {
"app@2x": "unpackage/res/icons/120x120.png",
"app@3x": "unpackage/res/icons/180x180.png",
"notification@2x": "unpackage/res/icons/40x40.png",
"notification@3x": "unpackage/res/icons/60x60.png",
"settings@2x": "unpackage/res/icons/58x58.png",
"settings@3x": "unpackage/res/icons/87x87.png",
"spotlight@2x": "unpackage/res/icons/80x80.png",
"spotlight@3x": "unpackage/res/icons/120x120.png"
}
}
},
"splashscreen": {
"useOriginalMsgbox": true
}
},
"nvueLaunchMode": "",
"nativePlugins": {
"usb-serial-for-uniapp": {
"__plugin_info__": {
"name": "usb-serial-for-uniapp",
"description": "uniapp的USB串口通信插件驱动基于开源项目usb-serial-for-android无需ROOT支持OTG USB串口、CP210X、CH34X等",
"platforms": "Android",
"url": "https://ext.dcloud.net.cn/plugin?id=13935",
"android_package_name": "uni.UNIFB29F21",
"ios_bundle_id": "",
"isCloud": true,
"bought": 1,
"pid": "13935",
"parameters": {}
}
}
}
},
"quickapp": {},
"mp-weixin": {
"appid": "",
"setting": {
"urlCheck": false
},
"usingComponents": true,
"unipush": {
"enable": false
}
},
"mp-alipay": {
"usingComponents": true
},
"mp-baidu": {
"usingComponents": true
},
"mp-toutiao": {
"usingComponents": true
},
"uniStatistics": {
"enable": false
},
"vueVersion": "3",
"locale": "zh-Hans",
"fallbackLocale": "zh-Hans"
}

View File

@@ -0,0 +1,27 @@
import { perms } from "@/utils/perms.js";
import { toPath } from "@/utils/utils.js";
import env from "@/utils/env";
/**
* @description: 获取文件路径
* @param {String} src 文件路径
* @return {String} 文件路径
*/
export function filePath(src :string) {
let path = "";
if (src) {
if (src.startsWith("http")) {
return src;
}
path = `${env.baseUrl}${src}`;
}
return path;
}
// 在全局配置对象中添加$perms属性并将其值设为perms
export function installMethods(app) {
app.config.globalProperties.$perms = perms;
app.config.globalProperties.$toPath = toPath;
app.config.globalProperties.$filePath = filePath;
}

12
x_admin_app/package.json Normal file
View File

@@ -0,0 +1,12 @@
{
"dependencies": {
"bignumber.js": "^9.1.2",
"crypto-js": "^4.2.0",
"dayjs": "^1.11.10",
"uuid": "^9.0.1"
},
"devDependencies": {
"@dcloudio/types": "^3.4.8",
"@uni-helper/uni-app-types": "^0.5.13"
}
}

84
x_admin_app/pages.json Normal file
View File

@@ -0,0 +1,84 @@
{
"pages": [{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "管理系统"
}
},
{
"path": "pages/login/login",
"style": {
"navigationBarTitleText": "登录"
}
},
{
"path": "pages/my/my",
"style": {
"navigationBarTitleText": "我的"
}
},
{
"path": "pages/monitor_project/index",
"style": {
"navigationBarTitleText": "项目监控",
"enablePullDownRefresh": true,
"onReachBottomDistance": 100,
"navigationStyle": "custom"
}
},
{
"path": "pages/monitor_project/details",
"style": {
"navigationBarTitleText": "项目监控详情",
"enablePullDownRefresh": true,
"navigationStyle": "custom"
}
},
{
"path": "pages/monitor_project/edit",
"style": {
"navigationBarTitleText": "编辑项目监控"
}
},
{
"path": "pages/monitor_project/search",
"style": {
"navigationBarTitleText": "搜索项目监控"
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "x admin",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#ccc",
"pageOrientation": "portrait"
},
"tabBar": {
"color": "#000000",
"selectedColor": "#007aff",
"borderStyle": "black",
"backgroundColor": "#ffffff",
"list": [
{
"pagePath": "pages/index/index",
"iconPath": "static/icons/tabs/home.png",
"selectedIconPath": "static/icons/tabs/homeActive.png",
"text": "首页"
},
{
"pagePath": "pages/my/my",
"iconPath": "static/icons/tabs/my.png",
"selectedIconPath": "static/icons/tabs/myActive.png",
"text": "我的"
}
]
}
}

View File

@@ -0,0 +1,121 @@
<template>
<view class="content">
<uv-swiper :list="list" circular indicator indicatorMode="dot"></uv-swiper>
<!-- <uv-notice-bar :text="text" direction="column"></uv-notice-bar> -->
<uv-grid :border="false">
<uv-grid-item v-for="(item, index) in baseList" :key="index" @click="toList(item)">
<uv-icon :customStyle="{ padding: '50rpx 0 20rpx' }" :name="item.icon" :size="26"></uv-icon>
<text class="grid-text">{{ item.title }}</text>
</uv-grid-item>
</uv-grid>
</view>
</template>
<script setup>
import {
ref
} from "vue";
import {
toPath,
toast,
scanCode,
queryToObj,
} from "@/utils/utils.js";
let list = [
"https://cdn.uviewui.com/uview/swiper/swiper1.png",
"https://cdn.uviewui.com/uview/swiper/swiper2.png",
"https://cdn.uviewui.com/uview/swiper/swiper3.png",
];
let text = ["Fota", "轻松管理"];
let baseList = [
// {
// icon: "/static/index/product.png",
// path: "/pages/product/product",
// title: "产品",
// },
// {
// icon: "/static/index/group.png",
// path: "/pages/group/group",
// title: "分组",
// },
// {
// icon: "/static/index/version.png",
// path: "/pages/version/version",
// title: "版本",
// },
{
icon: "/static/index/equipment.png",
path: "/pages/monitor_project/index",
title: "项目监控",
},
{
icon: "/static/index/equipment.png",
path: "/pages/equipment/equipment",
title: "设备管理",
},
{
icon: "scan",
// path: "/pages/equipment/equipment",
title: "扫码维护",
fn: function() {
// console.log('this', this);
scanCode().then((path) => {
// toPath(path);
if (!path) {
return
}
var query = queryToObj(path);
if (!query.state) {
toast("请扫描设备二维码");
return;
}
toPath("/pages/equipment/details", {
number: query.state,
});
});
},
},
{
icon: "/static/index/equipmentLog.png",
path: "/pages/statistics/statistics",
title: "销量统计",
},
// {
// icon: "/static/index/sysUp.png",
// path: "/pages/log/sysUp/sysUp",
// title: "上行日志",
// },
// {
// icon: "/static/index/sysDown.png",
// path: "/pages/log/sysDown/sysDown",
// title: "下行日志",
// },
// {
// icon: "/static/index/log.png",
// path: "/pages/log/sysLog/sysLog",
// title: "系统日志",
// },
];
function toList(item) {
if (item.fn) {
item.fn();
} else {
toPath(item.path);
}
}
</script>
<style>
.grid-text {
font-size: 28rpx;
}
</style>

View File

@@ -0,0 +1,176 @@
<template>
<view>
<uv-form
class="form"
ref="form"
labelWidth="60"
labelAlign="right"
:model="model"
:rules="rules"
>
<uv-form-item class="hide">
<uv-input
class="hide"
v-model="model.username"
placeholder="请输入账号"
:rules="[{ required: true, message: '请填写账号' }]"
/>
</uv-form-item>
<uv-form-item class="hide">
<uv-input
type="password"
v-model="model.password"
placeholder="请输入密码"
:rules="[{ required: true, message: '请填写密码' }]"
/>
</uv-form-item>
<uv-form-item label="账号" name="username" prop="username">
<uv-input
v-model="model.username"
placeholder="请输入账号"
:rules="[{ required: true, message: '请填写账号' }]"
/>
</uv-form-item>
<uv-form-item label="密码" name="password" prop="password">
<uv-input
type="password"
v-model="model.password"
placeholder="请输入密码"
:rules="[{ required: true, message: '请填写密码' }]"
/>
</uv-form-item>
<Verify
@success="VerifySuccess"
:mode="'pop'"
:captchaType="'blockPuzzle'"
ref="verifyRef"
:imgSize="{ width: '310px', height: '155px' }"
></Verify>
<view class="footer">
<!-- -->
<wd-button class="btn" size="large" @click="VerifyShow" block
>VerifyShow</wd-button
>
<wd-button class="btn" size="large" @click="handleSubmit" block
>登录</wd-button
>
</view>
</uv-form>
</view>
</template>
<script lang="ts" setup>
import { ref, reactive } from "vue";
import { v1 as uuid_v1 } from "uuid";
import Verify from "@/components/verifition/verify";
import { login } from "@/api/user.js";
import { setLocalStorage } from "@/utils/storage.js";
import { toast } from "@/utils/utils.js";
// import appicon from "@/static/appicon.png";
import { useUserStore } from "@/stores/user";
const userStore = useUserStore();
const model = reactive({
username: "",
password: "",
});
const rules = {
username: [
{
required: true,
message: "请填写账号",
trigger: ["blur", "change"],
},
],
password: [
{
required: true,
message: "请填写密码",
trigger: ["blur", "change"],
},
],
};
const form = ref();
const verifyRef=ref()
function VerifyShow(e) {
verifyRef.value.show()
}
function VerifySuccess(e) {
console.log('VerifySuccess',e);
handleSubmit(e)
}
function handleSubmit(verify) {
form.value
.validate()
.then((res) => {
let data = {
username: model.username,
password: model.password,
...verify
};
// userStore.getUser();
login(data)
.then((res) => {
if (res.code === 200) {
setLocalStorage("token", res.data.token);
userStore.getInfo().then((res) => {
uni.switchTab({
url: "/pages/index/index",
});
});
} else {
toast(res.message);
}
})
.catch((err) => {
toast(err.message);
});
})
.catch((err) => {
toast(err.message);
});
}
</script>
<style lang="scss" scoped>
.hide {
height: 0 !important;
padding: 0 !important;
border: 0 !important;
overflow: hidden !important;
}
.form {
margin: 200rpx 20rpx;
.captchaImage {
padding: 0 10rpx;
}
.footer {
padding: 60rpx 20rpx 200rpx;
.btn {
background-color: #00b294;
}
}
}
</style>

View File

@@ -0,0 +1,102 @@
<template>
<view class="page-content">
<uv-navbar leftText="" :placeholder="true" title="监控项目详情" autoBack>
<template v-slot:right>
<uv-icon name="edit-pen" size="20" @click="edit"></uv-icon>
</template>
</uv-navbar>
<uv-form labelPosition="left" labelWidth="80" :model="form">
<uv-form-item label="项目uuid" prop="projectKey" borderBottom>
{{form.projectKey}}
</uv-form-item>
<uv-form-item label="项目名称" prop="projectName" borderBottom>
{{form.projectName}}
</uv-form-item>
<uv-form-item label="项目类型" prop="projectType" borderBottom>
{{form.projectType}}
</uv-form-item>
<uv-form-item label="创建时间" prop="createTime" borderBottom>
{{form.createTime}}
</uv-form-item>
<uv-form-item label="更新时间" prop="updateTime" borderBottom>
{{form.updateTime}}
</uv-form-item>
</uv-form>
<uv-button
v-if="$perms('admin:monitor_project:edit')"
type="primary"
text="编辑"
customStyle="margin-top: 20rpx"
@click="edit"
></uv-button>
</view>
</template>
<script setup>
import {
reactive,
ref,
computed
} from "vue";
import {
onLoad,
onShow,
onPullDownRefresh
} from "@dcloudio/uni-app";
import {
useDictData
} from "@/hooks/useDictOptions";
import {
monitor_project_detail
} from "@/api/monitor_project";
import {
toast,
alert,
toPath
} from "@/utils/utils";
let form = ref({});
onLoad((e) => {
console.log("onLoad", e);
getDetails(e.id);
});
onShow((e) => {
if (form.value?.id) {
getDetails(form.value.id);
}
});
onPullDownRefresh(() => {
getDetails(form.value.id);
});
function getDetails(id) {
monitor_project_detail(id).then((res) => {
uni.stopPullDownRefresh();
if (res.code == 200) {
if (res?.data) {
form.value = res?.data
}
} else {
toast(res.message);
}
})
.catch((err) => {
uni.stopPullDownRefresh();
toast(err.message||"网络错误");
});
}
function edit() {
toPath("/pages/monitor_project/edit", { id: form.value.id });
}
</script>
<style lang="scss" scoped>
.page-content {
padding: 10rpx 20rpx 300rpx;
}
</style>

View File

@@ -0,0 +1,112 @@
<template>
<view class="page-content">
<uv-form
labelPosition="left"
labelWidth="80"
:model="form"
:rules="formRules"
ref="formRef"
>
<uv-form-item label="项目uuid" prop="projectKey" borderBottom>
<uv-input v-model="form.projectKey" border="surround"></uv-input>
</uv-form-item>
<uv-form-item label="项目名称" prop="projectName" borderBottom>
<uv-input v-model="form.projectName" border="surround"></uv-input>
</uv-form-item>
<uv-form-item label="项目类型" prop="projectType" borderBottom>
<x-picker v-model="form.projectType" valueKey="value" labelKey="name" :columns="dictData.project_type"></x-picker>
</uv-form-item>
<uv-button
type="primary"
text="提交"
customStyle="margin-top: 20rpx"
@click="submit"
></uv-button>
</uv-form>
</view>
</template>
<script setup>
import { reactive, ref, computed } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import {
monitor_project_detail,
monitor_project_edit,
} from "@/api/monitor_project";
import { toast, alert } from "@/utils/utils";
import { useDictData } from "@/hooks/useDictOptions";
const { dictData } = useDictData(['project_type'])
let formRef = ref();
let form = ref({
id: "",
projectKey: "",
projectName: "",
projectType: "",
});
const formRules = {
projectKey: [
{
required: true,
message: "请输入项目uuid",
trigger: ["blur"],
},
],
projectName: [
{
required: true,
message: "请输入项目名称",
trigger: ["blur"],
},
],
projectType: [
{
required: true,
message: "请选择项目类型",
trigger: ["blur"],
},
],
};
onLoad((e) => {
console.log("onLoad", e);
getDetails(e.id);
});
function getDetails(id) {
monitor_project_detail(id)
.then((res) => {
if (res.code == 200) {
if (res?.data) {
form.value = res?.data;
}
} else {
toast(res.message);
}
})
.catch((err) => {
toast(err.message || "网络错误");
});
}
function submit(item) {
console.log("submit", form);
formRef.value.validate().then(() => {
monitor_project_edit(form.value).then((res) => {
if (res.code == 200) {
toast("编辑成功");
getDetails(form.value?.id);
} else {
toast(res.message);
}
});
});
}
</script>
<style lang="scss" scoped>
.page-content {
padding: 10rpx 20rpx 300rpx;
}
</style>

View File

@@ -0,0 +1,119 @@
<template>
<view>
<uv-sticky :customNavHeight="0" bgColor="#fff">
<uv-status-bar></uv-status-bar>
<uv-navbar
leftText=""
:safeAreaInsetTop="false"
:fixed="false"
title="监控项目"
autoBack
>
<template v-slot:right>
<uv-icon v-if="!fromSearch" name="search" size="24" @click="moreSearch"></uv-icon>
</template>
</uv-navbar>
<!-- <view class="search">
<uv-search
placeholder="请输入搜索内容"
shape="square"
v-model="queryParams.key"
:showAction="false"
bgColor="#fff"
borderColor="rgba(0, 0, 0, .1)"
@search="resetPage"
></uv-search>
</view> -->
</uv-sticky>
<uv-list>
<uv-list-item
v-for="item of pager.lists"
:key="item.id"
clickable
show-arrow
:title="item.projectName"
:note="item.projectType"
@click="toDetails(item)"
></uv-list-item>
</uv-list>
<uv-back-top :scroll-top="scrollTop"></uv-back-top>
<uv-empty v-if="pager.loading =='nomore'&&pager.lists.length == 0" marginTop="150" mode="data"></uv-empty>
<uv-loading-page
:loading="pager.pageNo == 1 && pager.loading == 'loading'"
loading-text="加载中..."
font-size="24rpx"
></uv-loading-page>
<uv-load-more
v-if="pager.lists.length > 0"
:status="pager.loading"
:loading-text="pager.loadingText"
:loadmore-text="pager.loadmoreText"
:nomore-text="pager.nomoreText"
@loadmore="NextPage"
/>
</view>
</template>
<script setup>
import { reactive, ref } from "vue";
import {
onLoad,
onPullDownRefresh,
onReachBottom,
onPageScroll,
} from "@dcloudio/uni-app";
import { monitor_project_list } from "@/api/monitor_project";
import { usePaging } from "@/hooks/usePaging";
import { toPath } from "@/utils/utils";
const queryParams = reactive({
key: "",
});
let fromSearch=ref(false);
onLoad((e) => {
console.log("monitor_project onLoad", e);
if (e) {
for (const key in e) {
if (Object.hasOwnProperty.call(e, key)) {
fromSearch.value = true;
queryParams[key] = e[key];
}
}
}
getLists();
});
const { pager, getLists, NextPage, resetPage, resetParams } = usePaging({
fetchFun: monitor_project_list,
params: queryParams,
});
let scrollTop = ref(0);
onPageScroll((e) => {
scrollTop.value = e.scrollTop;
});
onPullDownRefresh(() => {
resetPage();
});
onReachBottom(() => {
NextPage();
});
function toDetails(item) {
toPath("/pages/monitor_project/details", { id: item.id });
}
function moreSearch() {
toPath("/pages/monitor_project/search");
}
</script>
<style lang="scss" scoped>
.search {
padding: 5rpx;
background-color: #fff;
}
</style>

View File

@@ -0,0 +1,75 @@
<template>
<view class="page-content">
<uv-form labelPosition="left" labelWidth="80" :model="form" ref="formRef">
<uv-form-item label="项目uuid" prop="projectKey" borderBottom>
<uv-input v-model="form.projectKey"> </uv-input>
</uv-form-item>
<uv-form-item label="项目名称" prop="projectName" borderBottom>
<uv-input v-model="form.projectName"> </uv-input>
</uv-form-item>
<uv-form-item
label="项目类型"
prop="projectType"
borderBottom
>
<x-picker v-model="form.projectType" valueKey="value" labelKey="name" :columns="dictData.project_type"></x-picker>
</uv-form-item>
<uv-form-item label="创建时间" prop="createTime" borderBottom>
<xDateRange
v-model:startTime="form.createTimeStart"
v-model:endTime="form.createTimeEnd"
></xDateRange>
</uv-form-item>
<uv-form-item label="更新时间" prop="updateTime" borderBottom>
<xDateRange
v-model:startTime="form.updateTimeStart"
v-model:endTime="form.updateTimeEnd"
></xDateRange>
</uv-form-item>
<uv-button
type="primary"
text="搜索"
customStyle="margin-top: 20rpx"
@click="submit"
></uv-button>
</uv-form>
</view>
</template>
<script setup>
import { onLoad } from "@dcloudio/uni-app";
import { reactive, ref, computed } from "vue";
import { toPath, toast, clearObjEmpty } from "@/utils/utils";
import { useDictData } from "@/hooks/useDictOptions";
import xDateRange from "@/components/x-date-range/x-date-range.vue"
const { dictData } = useDictData(['project_type'])
let formRef = ref();
let form = ref({
projectKey: "",
projectName: "",
projectType: "",
createTimeStart: "",
createTimeEnd: "",
updateTimeStart: "",
updateTimeEnd: "",
});
function submit() {
console.log("submit", form.value);
const search = clearObjEmpty(form.value);
if (Object.keys(search).length === 0) {
return toast("请输入查询条件");
}
toPath("/pages/monitor_project/index", search);
}
</script>
<style lang="scss" scoped>
.page-content {
padding: 10rpx 20rpx 300rpx;
}
</style>

View File

@@ -0,0 +1,63 @@
<template>
<view class="my">
<uv-sticky offsetTop="-1" bgColor="#ffffff">
<view style="background: #fff">
<uv-cell :title="userStore?.username" :label="userStore?.nickname" isLink>
<!-- -->
<template v-slot:icon>
<button class="avatar-wrapper" open-type="chooseAvatar" @click.stop>
<uv-avatar
width="100%"
height="100%"
shape="circle"
:src="$filePath(userStore.avatar)"
mode="widthFix"
></uv-avatar>
</button>
</template>
</uv-cell>
</view>
</uv-sticky>
<uv-button
class="btn"
type="primary"
text="登录"
@click="toLogin"
></uv-button>
</view>
</template>
<script setup>
import { useUserStore } from "@/stores/user";
const userStore = useUserStore();
function toLogin() {
uni.redirectTo({
url: "/pages/login/login",
});
}
</script>
<style scoped>
.my {
background-color: rgba(243, 246, 249, 1);
padding-bottom: 40px;
min-height: 100vh;
box-sizing: border-box;
}
.avatar-wrapper {
width: 100%;
height: 100%;
padding: 7px;
background-color: white;
border: 0;
outline: none;
}
.btn {
margin: 200rpx 20rpx;
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@@ -0,0 +1,77 @@
import { defineStore } from "pinia";
import { logout, getInfo } from "@/api/user";
import { getLocalStorage, removeLocalStorage } from "@/utils/storage.js";
export const useUserStore = defineStore("user", {
state: () => {
return {
nickname: "",
username: "",
avatar: "",
userInfo: {},
menu: [],
auth: [],
};
},
// 也可以这样定义
// state: () => ({ count: 0 })
actions: {
// get user info
getInfo() {
return new Promise((resolve, reject) => {
let token = getLocalStorage("token");
if (!token) {
return reject(new Error("需要登录"));
}
getInfo(token)
.then((res) => {
if (res.code === 200) {
const { data } = res;
this.nickname = data.user.nickname;
this.username = data.user.username;
this.avatar = data.user.avatar ||"";
// this.menu = data.menu;
// this.auth = data.auth;
this.userInfo = data.user;
resolve(data);
} else {
reject(new Error(res.message));
}
// addRoutes(result.menu);
})
.catch((error) => {
reject(error);
});
});
},
// user logout
logout() {
return new Promise((resolve, reject) => {
logout()
.then(() => {
// resetRouter();
this.resetToken();
resolve();
})
.catch((error) => {
reject(error);
});
});
},
// remove token
resetToken() {
return new Promise((resolve) => {
// this.token = "";
this.name = "";
this.avatar = "";
removeLocalStorage("token");
resolve();
});
},
},
});

29
x_admin_app/tsconfig.json Normal file
View File

@@ -0,0 +1,29 @@
{
"include": [
"global.d.ts",
"methods/**/*",
"api/**/*.ts",
"pages/**/*.vue",
"components/**/*.vue",
"stores/**/*",
"utils/**/*",
"typings/**/*.d.ts",
"hooks/usePaging.ts"
],
"exclude": ["dist", "node_modules", "uni_modules", "unpackage"],
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "Bundler",
"allowJs": true,
"checkJs": true,
"outDir": "./dist",
"isolatedModules": true,
"baseUrl": ".",
"paths": {
"@/*": ["./*"]
},
"types": ["@dcloudio/types", "@uni-helper/uni-app-types"]
}
}

7
x_admin_app/typings/methods.d.ts vendored Normal file
View File

@@ -0,0 +1,7 @@
declare module 'vue' {
interface ComponentCustomProperties {
$filePath: (typeof import('@/methods/index'))['filePath']
}
}
export {}

32
x_admin_app/typings/uniapp.d.ts vendored Normal file
View File

@@ -0,0 +1,32 @@
/// <reference types="@dcloudio/types" />
/// <reference types="@dcloudio/types" />
/// <reference types="@dcloudio/types" />
/// <reference types="@dcloudio/types" />
declare module '@dcloudio/uni-app' {
export const onShow: (callback: ((options?: App.LaunchShowOption) => void) | (() => void), target?: null) => Function;
export const onHide: (callback: (() => void) | (() => void), target?: null) => Function;
export const onLaunch: (callback: (options?: App.LaunchShowOption) => void, target?: null) => Function;
export const onError: (callback: (error: string) => void, target?: null) => Function;
export const onPageNotFound: (callback: (options: App.PageNotFoundOption) => void, target?: null) => Function;
export const onUnhandledRejection: (callback: (options: UniApp.OnUnhandledRejectionCallbackResult) => void, target?: null) => Function;
export const onThemeChange: (callback: (options: UniApp.OnThemeChangeCallbackResult) => void, target?: null) => Function;
export const onUniNViewMessage: (callback: (options: AnyObject) => void, target?: null) => Function;
export const onInit: (callback: (query?: AnyObject) => void, target?: null) => Function;
export const onLoad: (callback: (query?: AnyObject) => void, target?: null) => Function;
export const onReady: (callback: () => void, target?: null) => Function;
export const onUnload: (callback: () => void, target?: null) => Function;
export const onPullDownRefresh: (callback: () => void, target?: null) => Function;
export const onReachBottom: (callback: () => void, target?: null) => Function;
export const onShareAppMessage: (callback: (options: Page.ShareAppMessageOption) => Page.CustomShareContent, target?: null) => Function;
export const onShareTimeline: (callback: () => Page.ShareTimelineContent, target?: null) => Function;
export const onAddToFavorites: (callback: (options: Page.AddToFavoritesOption) => Page.CustomFavoritesContent, target?: null) => Function;
export const onPageScroll: (callback: (options: Page.PageScrollOption) => void, target?: null) => Function;
export const onResize: (callback: (options: Page.PageScrollOption) => void, target?: null) => Function;
export const onTabItemTap: (callback: (options: Page.TabItemTapOption) => void, target?: null) => Function;
export const onNavigationBarButtonTap: (callback: (options: Page.NavigationBarButtonTapOption) => void, target?: null) => Function;
export const onBackPress: (callback: (options: Page.BackPressOption) => any, target?: null) => Function;
export const onNavigationBarSearchInputChanged: (callback: (event: Page.NavigationBarSearchInputEvent) => void, target?: null) => Function;
export const onNavigationBarSearchInputConfirmed: (callback: (event: Page.NavigationBarSearchInputEvent) => void, target?: null) => Function;
export const onNavigationBarSearchInputClicked: (callback: () => void, target?: null) => Function;
}

View File

@@ -0,0 +1,10 @@
uni.addInterceptor({
returnValue (res) {
if (!(!!res && (typeof res === "object" || typeof res === "function") && typeof res.then === "function")) {
return res;
}
return new Promise((resolve, reject) => {
res.then((res) => res[0] ? reject(res[0]) : resolve(res[1]));
});
},
});

76
x_admin_app/uni.scss Normal file
View File

@@ -0,0 +1,76 @@
/**
* 这里是uni-app内置的常用样式变量
*
* uni-app 官方扩展插件及插件市场https://ext.dcloud.net.cn上很多三方插件均使用了这些样式变量
* 如果你是插件开发者建议你使用scss预处理并在插件代码中直接使用这些变量无需 import 这个文件方便用户通过搭积木的方式开发整体风格一致的App
*
*/
/**
* 如果你是App开发者插件使用者你可以通过修改这些变量来定制自己的插件主题实现自定义主题功能
*
* 如果你的项目同样使用了scss预处理你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
*/
// @import '@/uni_modules/uv-ui-tools/theme.scss';
/* 颜色变量 */
/* 行为相关颜色 */
$uni-color-primary: #007aff;
$uni-color-success: #4cd964;
$uni-color-warning: #f0ad4e;
$uni-color-error: #dd524d;
/* 文字基本颜色 */
$uni-text-color:#333;//基本色
$uni-text-color-inverse:#fff;//反色
$uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息
$uni-text-color-placeholder: #808080;
$uni-text-color-disable:#c0c0c0;
/* 背景颜色 */
$uni-bg-color:#ffffff;
$uni-bg-color-grey:#f8f8f8;
$uni-bg-color-hover:#f1f1f1;//点击状态颜色
$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
/* 边框颜色 */
$uni-border-color:#c8c7cc;
/* 尺寸变量 */
/* 文字尺寸 */
$uni-font-size-sm:12px;
$uni-font-size-base:14px;
$uni-font-size-lg:16px;
/* 图片尺寸 */
$uni-img-size-sm:20px;
$uni-img-size-base:26px;
$uni-img-size-lg:40px;
/* Border Radius */
$uni-border-radius-sm: 2px;
$uni-border-radius-base: 3px;
$uni-border-radius-lg: 6px;
$uni-border-radius-circle: 50%;
/* 水平间距 */
$uni-spacing-row-sm: 5px;
$uni-spacing-row-base: 10px;
$uni-spacing-row-lg: 15px;
/* 垂直间距 */
$uni-spacing-col-sm: 4px;
$uni-spacing-col-base: 8px;
$uni-spacing-col-lg: 12px;
/* 透明度 */
$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
/* 文章场景相关 */
$uni-color-title: #2C405A; // 文章标题颜色
$uni-font-size-title:20px;
$uni-color-subtitle: #555555; // 二级标题颜色
$uni-font-size-subtitle:26px;
$uni-color-paragraph: #3F536E; // 文章段落颜色
$uni-font-size-paragraph:15px;

View File

@@ -0,0 +1,7 @@
## 1.0.22023-07-02
uv-action-sheet 由于弹出层uv-popup的修改打开和关闭方法更改详情参考文档https://www.uvui.cn/components/actionSheet.html
## 1.0.12023-05-16
1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
2. 优化部分功能
## 1.0.02023-05-10
uv-action-sheet 底部操作菜单

View File

@@ -0,0 +1,50 @@
export default {
props: {
// 标题,有值则显示,同时会显示关闭按钮
title: {
type: String,
default: ''
},
// 选项上方的描述信息
description: {
type: String,
default: ''
},
// 数据
actions: {
type: Array,
default: () => []
},
// 取消按钮的文字,不为空时显示按钮
cancelText: {
type: String,
default: ''
},
// 点击某个菜单项时是否关闭弹窗
closeOnClickAction: {
type: Boolean,
default: true
},
// 处理底部安全区默认true
safeAreaInsetBottom: {
type: Boolean,
default: true
},
// 小程序的打开方式
openType: {
type: String,
default: ''
},
// 点击遮罩是否允许关闭 (默认true)
closeOnClickOverlay: {
type: Boolean,
default: true
},
// 圆角值
round: {
type: [Boolean, String, Number],
default: 0
},
...uni.$uv?.props?.actionSheet
}
}

View File

@@ -0,0 +1,280 @@
<template>
<uv-popup
ref="popup"
mode="bottom"
:safeAreaInsetBottom="safeAreaInsetBottom"
:round="round"
:close-on-click-overlay="closeOnClickOverlay"
@change="popupChange"
>
<view class="uv-action-sheet">
<view
class="uv-action-sheet__header"
v-if="title"
>
<text class="uv-action-sheet__header__title uv-line-1">{{title}}</text>
<view
class="uv-action-sheet__header__icon-wrap"
@tap.stop="cancel"
>
<uv-icon
name="close"
size="17"
color="#c8c9cc"
bold
></uv-icon>
</view>
</view>
<text
class="uv-action-sheet__description"
:style="[{
marginTop: `${title && description ? 0 : '18px'}`
}]"
v-if="description"
>{{description}}</text>
<slot>
<uv-line v-if="description"></uv-line>
<view class="uv-action-sheet__item-wrap">
<view v-for="(item, index) in actions" :key="index">
<!-- #ifdef MP -->
<button
class="uv-reset-button"
:openType="item.openType"
@getuserinfo="onGetUserInfo"
@contact="onContact"
@getphonenumber="onGetPhoneNumber"
@error="onError"
@launchapp="onLaunchApp"
@opensetting="onOpenSetting"
:lang="lang"
:session-from="sessionFrom"
:send-message-title="sendMessageTitle"
:send-message-path="sendMessagePath"
:send-message-img="sendMessageImg"
:show-message-card="showMessageCard"
:app-parameter="appParameter"
@tap="selectHandler(index)"
:hover-class="!item.disabled && !item.loading ? 'uv-action-sheet--hover' : ''"
>
<!-- #endif -->
<view
class="uv-action-sheet__item-wrap__item"
@tap.stop="selectHandler(index)"
:hover-class="!item.disabled && !item.loading ? 'uv-action-sheet--hover' : ''"
:hover-stay-time="150"
>
<template v-if="!item.loading">
<text
class="uv-action-sheet__item-wrap__item__name"
:style="[itemStyle(index)]"
>{{ item.name }}</text>
<text
v-if="item.subname"
class="uv-action-sheet__item-wrap__item__subname"
>{{ item.subname }}</text>
</template>
<uv-loading-icon
v-else
custom-class="van-action-sheet__loading"
size="18"
mode="circle"
/>
</view>
<!-- #ifdef MP -->
</button>
<!-- #endif -->
<uv-line v-if="index !== actions.length - 1"></uv-line>
</view>
</view>
</slot>
<uv-gap
bgColor="#eaeaec"
height="6"
v-if="cancelText"
></uv-gap>
<view hover-class="uv-action-sheet--hover">
<text
@touchmove.stop.prevent
:hover-stay-time="150"
v-if="cancelText"
class="uv-action-sheet__cancel-text"
@tap="cancel"
>{{cancelText}}</text>
</view>
</view>
</uv-popup>
</template>
<script>
import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
import button from '@/uni_modules/uv-ui-tools/libs/mixin/button.js'
import openType from '@/uni_modules/uv-ui-tools/libs/mixin/openType.js'
import props from './props.js';
/**
* ActionSheet 操作菜单
* @description 本组件用于从底部弹出一个操作菜单供用户选择并返回结果。本组件功能类似于uni的uni.showActionSheetAPI配置更加灵活所有平台都表现一致。
* @tutorial https://www.uvui.cn/components/actionSheet.html
* @property {Boolean} show 操作菜单是否展示 (默认 false
* @property {String} title 操作菜单标题
* @property {String} description 选项上方的描述信息
* @property {Array<Object>} actions 按钮的文字数组,见官方文档示例
* @property {String} cancelText 取消按钮的提示文字,不为空时显示按钮
* @property {Boolean} closeOnClickAction 点击某个菜单项时是否关闭弹窗 (默认 true
* @property {Boolean} safeAreaInsetBottom 处理底部安全区 (默认 true
* @property {String} openType 小程序的打开方式 (contact | launchApp | getUserInfo | openSetting getPhoneNumber error )
* @property {Boolean} closeOnClickOverlay 点击遮罩是否允许关闭 (默认 true )
* @property {String} lang 指定返回用户信息的语言zh_CN 简体中文zh_TW 繁体中文en 英文
* @property {String} sessionFrom 会话来源openType="contact"时有效
* @property {String} sendMessageTitle 会话内消息卡片标题openType="contact"时有效
* @property {String} sendMessagePath 会话内消息卡片点击跳转小程序路径openType="contact"时有效
* @property {String} sendMessageImg 会话内消息卡片图片openType="contact"时有效
* @property {Boolean} showMessageCard 是否显示会话内消息卡片,设置此参数为 true用户进入客服会话会在右下角显示"可能要发送的小程序"提示用户点击后可以快速发送小程序消息openType="contact"时有效 (默认 false
* @property {String} appParameter 打开 APP 时,向 APP 传递的参数openType=launchApp 时有效
*
* @event {Function} select 点击ActionSheet列表项时触发
* @event {Function} close 点击取消按钮时触发
* @event {Function} getuserinfo 用户点击该按钮时,会返回获取到的用户信息,回调的 detail 数据与 wx.getUserInfo 返回的一致openType="getUserInfo"时有效
* @event {Function} contact 客服消息回调openType="contact"时有效
* @event {Function} getphonenumber 获取用户手机号回调openType="getPhoneNumber"时有效
* @event {Function} error 当使用开放能力时发生错误的回调openType="error"时有效
* @event {Function} launchapp 打开 APP 成功的回调openType="launchApp"时有效
* @event {Function} opensetting 在打开授权设置页后回调openType="openSetting"时有效
* @example <uv-action-sheet ref="actionSheet" :actions="list" :title="title" ></uv-action-sheet>
*/
export default {
name: "uv-action-sheet",
mixins: [openType, button, mpMixin , mixin, props],
emits: ['close', 'select'],
computed: {
// 操作项目的样式
itemStyle() {
return (index) => {
let style = {};
if (this.actions[index].color) style.color = this.actions[index].color
if (this.actions[index].fontSize) style.fontSize = this.$uv.addUnit(this.actions[index].fontSize)
// 选项被禁用的样式
if (this.actions[index].disabled) style.color = '#c0c4cc'
return style;
}
},
},
methods: {
open() {
this.$refs.popup.open();
},
close() {
this.$refs.popup.close();
},
popupChange(e) {
if(!e.show) this.$emit('close');
},
// 点击取消按钮
cancel() {
this.close();
},
selectHandler(index) {
const item = this.actions[index]
if (item && !item.disabled && !item.loading) {
this.$emit('select', item)
if (this.closeOnClickAction) {
this.close();
}
}
},
}
}
</script>
<style lang="scss" scoped>
$show-lines: 1;
$show-reset-button: 1;
@import '@/uni_modules/uv-ui-tools/libs/css/variable.scss';
@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
@import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
$uv-action-sheet-reset-button-width:100% !default;
$uv-action-sheet-title-font-size: 16px !default;
$uv-action-sheet-title-padding: 12px 30px !default;
$uv-action-sheet-title-color: $uv-main-color !default;
$uv-action-sheet-header-icon-wrap-right:15px !default;
$uv-action-sheet-header-icon-wrap-top:15px !default;
$uv-action-sheet-description-font-size:13px !default;
$uv-action-sheet-description-color:14px !default;
$uv-action-sheet-description-margin: 18px 15px !default;
$uv-action-sheet-item-wrap-item-padding:15px !default;
$uv-action-sheet-item-wrap-name-font-size:16px !default;
$uv-action-sheet-item-wrap-subname-font-size:13px !default;
$uv-action-sheet-item-wrap-subname-color: #c0c4cc !default;
$uv-action-sheet-item-wrap-subname-margin-top:10px !default;
$uv-action-sheet-cancel-text-font-size:16px !default;
$uv-action-sheet-cancel-text-color:$uv-content-color !default;
$uv-action-sheet-cancel-text-font-size:15px !default;
$uv-action-sheet-cancel-text-hover-background-color:rgb(242, 243, 245) !default;
.uv-reset-button {
width: $uv-action-sheet-reset-button-width;
}
.uv-action-sheet {
text-align: center;
&__header {
position: relative;
padding: $uv-action-sheet-title-padding;
&__title {
font-size: $uv-action-sheet-title-font-size;
color: $uv-action-sheet-title-color;
font-weight: bold;
text-align: center;
}
&__icon-wrap {
position: absolute;
right: $uv-action-sheet-header-icon-wrap-right;
top: $uv-action-sheet-header-icon-wrap-top;
}
}
&__description {
font-size: $uv-action-sheet-description-font-size;
color: $uv-tips-color;
margin: $uv-action-sheet-description-margin;
text-align: center;
}
&__item-wrap {
&__item {
padding: $uv-action-sheet-item-wrap-item-padding;
@include flex;
align-items: center;
justify-content: center;
flex-direction: column;
&__name {
font-size: $uv-action-sheet-item-wrap-name-font-size;
color: $uv-main-color;
text-align: center;
}
&__subname {
font-size: $uv-action-sheet-item-wrap-subname-font-size;
color: $uv-action-sheet-item-wrap-subname-color;
margin-top: $uv-action-sheet-item-wrap-subname-margin-top;
text-align: center;
}
}
}
&__cancel-text {
font-size: $uv-action-sheet-cancel-text-font-size;
color: $uv-action-sheet-cancel-text-color;
text-align: center;
padding: $uv-action-sheet-cancel-text-font-size;
}
&--hover {
background-color: $uv-action-sheet-cancel-text-hover-background-color;
}
}
</style>

View File

@@ -0,0 +1,92 @@
{
"id": "uv-action-sheet",
"displayName": "uv-action-sheet 底部操作菜单 全面兼容小程序、nvue、vue2、vue3等多端",
"version": "1.0.2",
"description": "该组件用于从底部弹出一个操作菜单供用户选择并返回结果。本组件功能类似于uni的uni.showActionSheet API配置更加灵活所有平台都表现一致。",
"keywords": [
"action-sheet",
"uvui",
"uv-ui",
"操作菜单",
"菜单选择"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [
"uv-ui-tools",
"uv-popup",
"uv-icon",
"uv-line",
"uv-loading-icon",
"uv-gap"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

View File

@@ -0,0 +1,13 @@
## ActionSheet 操作菜单
> **组件名uv-action-sheet**
本组件用于从底部弹出一个操作菜单,供用户选择并返回结果。
本组件功能类似于uni的uni.showActionSheet API配置更加灵活所有平台都表现一致。
### <a href="https://www.uvui.cn/components/actionSheet.html" target="_blank">查看文档</a>
### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
#### 如使用过程中有任何问题或者您对uv-ui有一些好的建议欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>

View File

@@ -0,0 +1,10 @@
## 1.0.42023-12-06
1. 阻止事件冒泡处理
## 1.0.32023-10-23
1. 修复报错的BUG
## 1.0.22023-10-23
1. 修复设置singleSize、multipleSize、space等值带单位存在不显示的BUG
## 1.0.12023-09-13
1. 添加依赖
## 1.0.02023-08-30
1. 新增uv-album相册组件

View File

@@ -0,0 +1,312 @@
<template>
<view class="uv-album">
<view
class="uv-album__row"
ref="uv-album__row"
v-for="(arr, index) in showUrls"
:forComputedUse="albumWidth"
:key="index"
>
<view
class="uv-album__row__wrapper"
v-for="(item, index1) in arr"
:key="index1"
:style="[imageStyle(index + 1, index1 + 1)]"
@tap.stop="previewFullImage ? onPreviewTap(getSrc(item)) : ''"
>
<image
:src="getSrc(item)"
:mode="
urls.length === 1
? imageHeight > 0
? singleMode
: 'widthFix'
: multipleMode
"
:style="[
{
width: imageWidth,
height: imageHeight
}
]"
></image>
<view
v-if="
showMore &&
urls.length > rowCount * showUrls.length &&
index === showUrls.length - 1 &&
index1 === showUrls[showUrls.length - 1].length - 1
"
class="uv-album__row__wrapper__text"
>
<uv-text
:text="`+${urls.length - maxCount}`"
color="#fff"
:size="$uv.getPx(multipleSize) * 0.3"
align="center"
customStyle="justify-content: center"
></uv-text>
</view>
</view>
</view>
</view>
</template>
<script>
import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
// #ifdef APP-NVUE
// 由于weex为阿里的KPI业绩考核的产物所以不支持百分比单位这里需要通过dom查询组件的宽度
const dom = uni.requireNativePlugin('dom')
// #endif
/**
* Album 相册
* @description 本组件提供一个类似相册的功能,让开发者开发起来更加得心应手。减少重复的模板代码
* @tutorial https://www.uvui.cn/components/album.html
* @property {Array} urls 图片地址列表 Array<String>|Array<Object>形式
* @property {String} keyName 指定从数组的对象元素中读取哪个属性作为图片地址
* @property {String | Number} singleSize 单图时,图片长边的长度 (默认 180
* @property {String | Number} multipleSize 多图时,图片边长 (默认 70
* @property {String | Number} space 多图时,图片水平和垂直之间的间隔 (默认 6
* @property {String} singleMode 单图时,图片缩放裁剪的模式 (默认 'scaleToFill'
* @property {String} multipleMode 多图时,图片缩放裁剪的模式 (默认 'aspectFill'
* @property {String | Number} maxCount 取消按钮的提示文字 (默认 9
* @property {Boolean} previewFullImage 是否可以预览图片 (默认 true
* @property {String | Number} rowCount 每行展示图片数量如设置singleSize和multipleSize将会无效 (默认 3
* @property {Boolean} showMore 超出maxCount时是否显示查看更多的提示 (默认 true
*
* @event {Function} albumWidth 某些特殊的情况下,需要让文字与相册的宽度相等,这里事件的形式对外发送 (回调参数 width
* @example <uv-album :urls="urls2" @albumWidth="width => albumWidth = width" multipleSize="68" ></uv-album>
*/
export default {
name: 'uv-album',
mixins: [mpMixin, mixin],
emits: ['albumWidth'],
props: {
// 图片地址Array<String>|Array<Object>形式
urls: {
type: Array,
default: () => []
},
// 指定从数组的对象元素中读取哪个属性作为图片地址
keyName: {
type: String,
default: ''
},
// 单图时,图片长边的长度
singleSize: {
type: [String, Number],
default: 180
},
// 多图时,图片边长
multipleSize: {
type: [String, Number],
default: 70
},
// 多图时,图片水平和垂直之间的间隔
space: {
type: [String, Number],
default: 6
},
// 单图时,图片缩放裁剪的模式
singleMode: {
type: String,
default: 'scaleToFill'
},
// 多图时,图片缩放裁剪的模式
multipleMode: {
type: String,
default: 'aspectFill'
},
// 最多展示的图片数量,超出时最后一个位置将会显示剩余图片数量
maxCount: {
type: [String, Number],
default: 9
},
// 是否可以预览图片
previewFullImage: {
type: Boolean,
default: true
},
// 每行展示图片数量如设置singleSize和multipleSize将会无效
rowCount: {
type: [String, Number],
default: 3
},
// 超出maxCount时是否显示查看更多的提示
showMore: {
type: Boolean,
default: true
},
...uni.$uv?.props?.album
},
data() {
return {
// 单图的宽度
singleWidth: 0,
// 单图的高度
singleHeight: 0,
// 单图时,如果无法获取图片的尺寸信息,让图片宽度默认为容器的一定百分比
singlePercent: 0.6
}
},
watch: {
urls: {
immediate: true,
handler(newVal) {
if (newVal.length === 1) {
this.getImageRect()
}
}
}
},
computed: {
imageStyle() {
return (index1, index2) => {
const { space, rowCount, multipleSize, urls } = this;
const rowLen = this.showUrls.length;
const allLen = this.urls.length;
const style = {
marginRight: this.$uv.addUnit(space),
marginBottom: this.$uv.addUnit(space)
}
// 如果为最后一行,则每个图片都无需下边框
if (index1 === rowLen) style.marginBottom = 0
// 每行的最右边一张和总长度的最后一张无需右边框
if (
index2 === rowCount ||
(index1 === rowLen &&
index2 === this.showUrls[index1 - 1].length)
)
style.marginRight = 0
return style
}
},
// 将数组划分为二维数组
showUrls() {
const arr = []
this.urls.map((item, index) => {
// 限制最大展示数量
if (index + 1 <= this.maxCount) {
// 计算该元素为第几个素组内
const itemIndex = Math.floor(index / this.rowCount)
// 判断对应的索引是否存在
if (!arr[itemIndex]) {
arr[itemIndex] = []
}
arr[itemIndex].push(item)
}
})
return arr
},
imageWidth() {
return this.$uv.addUnit(
this.urls.length === 1 ? this.singleWidth : this.multipleSize
)
},
imageHeight() {
return this.$uv.addUnit(
this.urls.length === 1 ? this.singleHeight : this.multipleSize
)
},
// 此变量无实际用途仅仅是为了利用computed特性让其在urls长度等变化时重新计算图片的宽度
// 因为用户在某些特殊的情况下,需要让文字与相册的宽度相等,所以这里事件的形式对外发送
albumWidth() {
let width = 0
if (this.urls.length === 1) {
width = this.singleWidth
} else {
width =
this.showUrls[0].length * this.$uv.getPx(this.multipleSize) +
this.$uv.getPx(this.space) * (this.showUrls[0].length - 1)
}
this.$emit('albumWidth', width)
return width
}
},
methods: {
// 预览图片
onPreviewTap(url) {
const urls = this.urls.map((item) => {
return this.getSrc(item)
})
uni.previewImage({
current: url,
urls
})
},
// 获取图片的路径
getSrc(item) {
return this.$uv.test.object(item) ?
(this.keyName && item[this.keyName]) || item.src :
item
},
// 单图时,获取图片的尺寸
// 在小程序中需要将网络图片的的域名添加到小程序的download域名才可能获取尺寸
// 在没有添加的情况下,让单图宽度默认为盒子的一定宽度(singlePercent)
getImageRect() {
const src = this.getSrc(this.urls[0])
uni.getImageInfo({
src,
success: (res) => {
// 判断图片横向还是竖向展示方式
const isHorizotal = res.width >= res.height
this.singleWidth = isHorizotal ?
this.singleSize :
(res.width / res.height) * this.$uv.getPx(this.singleSize)
this.singleHeight = !isHorizotal ?
this.singleSize :
(res.height / res.width) * this.singleWidth
},
fail: () => {
this.getComponentWidth()
}
})
},
// 获取组件的宽度
async getComponentWidth() {
// 延时一定时间以获取dom尺寸
await this.$uv.sleep(30)
// #ifndef APP-NVUE
this.$uGetRect('.uv-album__row').then((size) => {
this.singleWidth = size.width * this.singlePercent
})
// #endif
// #ifdef APP-NVUE
// 这里ref="uv-album__row"所在的标签为通过for循环出来导致this.$refs['uv-album__row']是一个数组
const ref = this.$refs['uv-album__row'][0]
ref &&
dom.getComponentRect(ref, (res) => {
this.singleWidth = res.size.width * this.singlePercent
})
// #endif
}
}
}
</script>
<style lang="scss" scoped>
@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
.uv-album {
@include flex(column);
&__row {
@include flex(row);
flex-wrap: wrap;
&__wrapper {
position: relative;
&__text {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.3);
@include flex(row);
justify-content: center;
align-items: center;
}
}
}
}
</style>

View File

@@ -0,0 +1,88 @@
{
"id": "uv-album",
"displayName": "uv-album 相册 全面兼容vue3+2、app、h5、小程序等多端",
"version": "1.0.4",
"description": "本组件提供一个类似相册的功能,让开发者开发起来更加得心应手,功能齐全,灵活配置可以,开箱即用。减少重复的模板代码",
"keywords": [
"album",
"uv-ui",
"uvui",
"相册",
"图片"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [
"uv-ui-tools",
"uv-text"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

View File

@@ -0,0 +1,21 @@
# Album 相册
> **组件名uv-album**
本组件提供一个类似相册的功能,让开发者开发起来更加得心应手。
功能齐全,灵活配置可以,开箱即用。减少重复的模板代码。
# <a href="https://www.uvui.cn/components/album.html" target="_blank">查看文档</a>
## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP</small>
### [更多插件请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
![image](https://mp-a667b617-c5f1-4a2d-9a54-683a67cff588.cdn.bspapp.com/uv-ui/banner.png)
</a>
#### 如使用过程中有任何问题反馈或者您对uv-ui有一些好的建议欢迎加入uv-ui官方交流群<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>

View File

@@ -0,0 +1,7 @@
## 1.0.22023-06-01
1. 修复点击触发两次实践的BUG
## 1.0.12023-05-16
1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
2. 优化部分功能
## 1.0.02023-05-10
uv-alert 警告提示组件

View File

@@ -0,0 +1,45 @@
export default {
props: {
// 显示文字
title: {
type: String,
default: ''
},
// 主题success/warning/info/error
type: {
type: String,
default: 'warning'
},
// 辅助性文字
description: {
type: String,
default: ''
},
// 是否可关闭
closable: {
type: Boolean,
default: false
},
// 是否显示图标
showIcon: {
type: Boolean,
default: false
},
// 浅或深色调light-浅色dark-深色
effect: {
type: String,
default: 'light'
},
// 文字是否居中
center: {
type: Boolean,
default: false
},
// 字体大小
fontSize: {
type: [String, Number],
default: 14
},
...uni.$uv?.props?.alert
}
}

View File

@@ -0,0 +1,246 @@
<template>
<uv-transition
mode="fade"
:show="show"
>
<view
class="uv-alert"
:class="[`uv-alert--${type}--${effect}`]"
@tap.stop="clickHandler"
:style="[$uv.addStyle(customStyle)]"
>
<view
class="uv-alert__icon"
v-if="showIcon"
>
<uv-icon
:name="iconName"
size="18"
:color="iconColor"
></uv-icon>
</view>
<view
class="uv-alert__content"
:style="[{
paddingRight: closable ? '20px' : 0
}]"
>
<text
class="uv-alert__content__title"
v-if="title"
:style="[{
fontSize: $uv.addUnit(fontSize),
textAlign: center ? 'center' : 'left'
}]"
:class="[effect === 'dark' ? 'uv-alert__text--dark' : `uv-alert__text--${type}--light`]"
>{{ title }}</text>
<text
class="uv-alert__content__desc"
v-if="description"
:style="[{
fontSize: $uv.addUnit(fontSize),
textAlign: center ? 'center' : 'left'
}]"
:class="[effect === 'dark' ? 'uv-alert__text--dark' : `uv-alert__text--${type}--light`]"
>{{ description }}</text>
</view>
<view
class="uv-alert__close"
v-if="closable"
@tap.stop="closeHandler"
>
<uv-icon
name="close"
:color="iconColor"
size="15"
></uv-icon>
</view>
</view>
</uv-transition>
</template>
<script>
import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
import props from './props.js';
/**
* Alert 警告提示
* @description 警告提示,展现需要关注的信息。
* @tutorial https://www.uvui.cn/components/alertTips.html
*
* @property {String} title 显示的文字
* @property {String} type 使用预设的颜色 (默认 'warning'
* @property {String} description 辅助性文字颜色比title浅一点字号也小一点可选
* @property {Boolean} closable 关闭按钮(默认为叉号icon图标) (默认 false
* @property {Boolean} showIcon 是否显示左边的辅助图标 默认 false
* @property {String} effect 多图时,图片缩放裁剪的模式 (默认 'light'
* @property {Boolean} center 文字是否居中 (默认 false
* @property {String | Number} fontSize 字体大小 (默认 14
* @property {Object} customStyle 定义需要用到的外部样式
* @event {Function} click 点击组件时触发
* @example <uv-alert :title="title" type = "warning" :closable="closable" :description = "description"></uv-alert>
*/
export default {
name: 'uv-alert',
mixins: [mpMixin, mixin, props],
emits: ['click'],
data() {
return {
show: true
}
},
computed: {
iconColor() {
return this.effect === 'light' ? this.type : '#fff'
},
// 不同主题对应不同的图标
iconName() {
switch (this.type) {
case 'success':
return 'checkmark-circle-fill';
break;
case 'error':
return 'close-circle-fill';
break;
case 'warning':
return 'error-circle-fill';
break;
case 'info':
return 'info-circle-fill';
break;
case 'primary':
return 'more-circle-fill';
break;
default:
return 'error-circle-fill';
}
}
},
methods: {
// 点击内容
clickHandler() {
this.$emit('click')
},
// 点击关闭按钮
closeHandler() {
this.show = false
}
}
}
</script>
<style lang="scss" scoped>
@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
@import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
.uv-alert {
position: relative;
background-color: $uv-primary;
padding: 8px 10px;
@include flex(row);
align-items: center;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
&--primary--dark {
background-color: $uv-primary;
}
&--primary--light {
background-color: #ecf5ff;
}
&--error--dark {
background-color: $uv-error;
}
&--error--light {
background-color: #FEF0F0;
}
&--success--dark {
background-color: $uv-success;
}
&--success--light {
background-color: #f5fff0;
}
&--warning--dark {
background-color: $uv-warning;
}
&--warning--light {
background-color: #FDF6EC;
}
&--info--dark {
background-color: $uv-info;
}
&--info--light {
background-color: #f4f4f5;
}
&__icon {
margin-right: 5px;
}
&__content {
@include flex(column);
flex: 1;
&__title {
color: $uv-main-color;
font-size: 14px;
font-weight: bold;
color: #fff;
margin-bottom: 2px;
}
&__desc {
color: $uv-main-color;
font-size: 14px;
flex-wrap: wrap;
color: #fff;
}
}
&__title--dark,
&__desc--dark {
color: #FFFFFF;
}
&__text--primary--light,
&__text--primary--light {
color: $uv-primary;
}
&__text--success--light,
&__text--success--light {
color: $uv-success;
}
&__text--warning--light,
&__text--warning--light {
color: $uv-warning;
}
&__text--error--light,
&__text--error--light {
color: $uv-error;
}
&__text--info--light,
&__text--info--light {
color: $uv-info;
}
&__close {
position: absolute;
top: 11px;
right: 10px;
}
}
</style>

View File

@@ -0,0 +1,88 @@
{
"id": "uv-alert",
"displayName": "uv-alert 警告提示 全面兼容小程序、nvue、vue2、vue3等多端",
"version": "1.0.2",
"description": "uv-alert 警告提示,展现需要关注的信息。灵活配置,功能齐全,兼容全端",
"keywords": [
"alert",
"uvui",
"uv-ui",
"警告提示"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [
"uv-ui-tools",
"uv-transition",
"uv-icon"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

View File

@@ -0,0 +1,15 @@
## Alert 警告提示
> **组件名uv-alert**
警告提示,展现需要关注的信息。
当某个页面需要向用户显示警告的信息时。
非浮层的静态展现形式,始终展现,不会自动消失,用户可以点击关闭。
### <a href="https://www.uvui.cn/components/alert.html" target="_blank">查看文档</a>
### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
#### 如使用过程中有任何问题或者您对uv-ui有一些好的建议欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>

View File

@@ -0,0 +1,13 @@
## 1.0.52023-12-06
1. 优化
## 1.0.42023-12-06
1. 优化
## 1.0.32023-12-06
1. 阻止事件冒泡处理,单个头像模式
## 1.0.22023-12-06
1. 阻止事件冒泡处理
## 1.0.12023-05-16
1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
2. 优化部分功能
## 1.0.02023-05-10
uv-avatar 头像组件

View File

@@ -0,0 +1,53 @@
export default {
props: {
// 头像图片组
urls: {
type: Array,
default: () => []
},
// 最多展示的头像数量
maxCount: {
type: [String, Number],
default: 5
},
// 头像形状
shape: {
type: String,
default: 'circle'
},
// 图片裁剪模式
mode: {
type: String,
default: 'scaleToFill'
},
// 超出maxCount时是否显示查看更多的提示
showMore: {
type: Boolean,
default: true
},
// 头像大小
size: {
type: [String, Number],
default: 40
},
// 指定从数组的对象元素中读取哪个属性作为图片地址
keyName: {
type: String,
default: ''
},
// 头像之间的遮挡比例
gap: {
type: [String, Number],
validator(value) {
return value >= 0 && value <= 1
},
default: 0.5
},
// 需额外显示的值
extraValue: {
type: [Number, String],
default: 0
},
...uni.$uv?.props?.avatarGroup
}
}

View File

@@ -0,0 +1,106 @@
<template>
<view class="uv-avatar-group">
<view
class="uv-avatar-group__item"
v-for="(item, index) in showUrl"
:key="index"
:style="{
marginLeft: index === 0 ? 0 : $uv.addUnit(-size * gap)
}"
>
<uv-avatar
:size="size"
:shape="shape"
:mode="mode"
:src="$uv.test.object(item) ? keyName && item[keyName] || item.url : item"
></uv-avatar>
<view
class="uv-avatar-group__item__show-more"
v-if="showMore && index === showUrl.length - 1 && (urls.length > maxCount || extraValue > 0)"
@tap="clickHandler"
>
<uv-text
color="#ffffff"
:size="size * 0.4"
:text="`+${extraValue || urls.length - showUrl.length}`"
align="center"
customStyle="justify-content: center"
></uv-text>
</view>
</view>
</view>
</template>
<script>
import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
import props from './props.js';
/**
* AvatarGroup 头像组
* @description 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。
* @tutorial https://www.uvui.cn/components/avatar.html
*
* @property {Array} urls 头像图片组 (默认 []
* @property {String | Number} maxCount 最多展示的头像数量 默认 5
* @property {String} shape 头像形状( 'circle' (默认) | 'square'
* @property {String} mode 图片裁剪模式(默认 'scaleToFill'
* @property {Boolean} showMore 超出maxCount时是否显示查看更多的提示 (默认 true
* @property {String | Number} size 头像大小 (默认 40
* @property {String} keyName 指定从数组的对象元素中读取哪个属性作为图片地址
* @property {String | Number} gap 头像之间的遮挡比例0.4代表遮挡40% (默认 0.5
* @property {String | Number} extraValue 需额外显示的值
* @event {Function} showMore 头像组更多点击
* @example <uv-avatar-group:urls="urls" size="35" gap="0.4" ></uv-avatar-group:urls=>
*/
export default {
name: 'uv-avatar-group',
mixins: [mpMixin, mixin, props],
data() {
return {
}
},
computed: {
showUrl() {
return this.urls.slice(0, this.maxCount)
}
},
methods: {
clickHandler() {
this.$emit('showMore')
}
},
}
</script>
<style lang="scss" scoped>
@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
@import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
.uv-avatar-group {
@include flex;
&__item {
margin-left: -10px;
position: relative;
&--no-indent {
// 如果你想质疑作者不会使用:first-child说明你太年轻因为nvue不支持
margin-left: 0;
}
&__show-more {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.3);
@include flex;
align-items: center;
justify-content: center;
border-radius: 100px;
}
}
}
</style>

View File

@@ -0,0 +1,80 @@
import { range } from '@/uni_modules/uv-ui-tools/libs/function/test.js'
export default {
props: {
// 头像图片路径(不能为相对路径)
src: {
type: String,
default: ''
},
// 头像形状circle-圆形square-方形
shape: {
type: String,
default: 'circle'
},
// 头像尺寸
size: {
type: [String, Number],
default: 40
},
// 裁剪模式
mode: {
type: String,
default: 'scaleToFill'
},
// 显示的文字
text: {
type: String,
default: ''
},
// 背景色
bgColor: {
type: String,
default: '#c0c4cc'
},
// 文字颜色
color: {
type: String,
default: '#fff'
},
// 文字大小
fontSize: {
type: [String, Number],
default: 18
},
// 显示的图标
icon: {
type: String,
default: ''
},
// 显示小程序头像只对百度微信QQ小程序有效
mpAvatar: {
type: Boolean,
default: false
},
// 是否使用随机背景色
randomBgColor: {
type: Boolean,
default: false
},
// 加载失败的默认头像(组件有内置默认图片)
defaultUrl: {
type: String,
default: ''
},
// 如果配置了randomBgColor为true且配置了此值则从默认的背景色数组中取出对应索引的颜色值取值0-19之间
colorIndex: {
type: [String, Number],
// 校验参数规则索引在0-19之间
validator(n) {
return range(n, [0, 19]) || n === ''
},
default: ''
},
// 组件标识符
name: {
type: String,
default: ''
},
...uni.$uv?.props?.avatar
}
}

View File

@@ -0,0 +1,175 @@
<template>
<view
class="uv-avatar"
:class="[`uv-avatar--${shape}`]"
:style="[{
backgroundColor: (text || icon) ? (randomBgColor ? colors[colorIndex !== '' ? colorIndex : $uv.random(0, 19)] : bgColor) : 'transparent',
width: $uv.addUnit(size),
height: $uv.addUnit(size),
}, $uv.addStyle(customStyle)]"
@tap="clickHandler"
>
<slot>
<!-- #ifdef MP-WEIXIN || MP-QQ || MP-BAIDU -->
<open-data
v-if="mpAvatar && allowMp"
type="userAvatarUrl"
:style="[{
width: $uv.addUnit(size),
height: $uv.addUnit(size)
}]"
/>
<!-- #endif -->
<!-- #ifndef MP-WEIXIN && MP-QQ && MP-BAIDU -->
<template v-if="mpAvatar && allowMp"></template>
<!-- #endif -->
<uv-icon
v-else-if="icon"
:name="icon"
:size="fontSize"
:color="color"
></uv-icon>
<uv-text
v-else-if="text"
:text="text"
:size="fontSize"
:color="color"
align="center"
customStyle="justify-content: center"
></uv-text>
<image
class="uv-avatar__image"
v-else
:class="[`uv-avatar__image--${shape}`]"
:src="avatarUrl || defaultUrl"
:mode="mode"
@error="errorHandler"
:style="[{
width: $uv.addUnit(size),
height: $uv.addUnit(size)
}]"
></image>
</slot>
</view>
</template>
<script>
import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
import props from './props.js';
const base64Avatar =
"data:image/jpg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAA8AAD/4QMraHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjMtYzAxMSA2Ni4xNDU2NjEsIDIwMTIvMDIvMDYtMTQ6NTY6MjcgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzYgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjREMEQwRkY0RjgwNDExRUE5OTY2RDgxODY3NkJFODMxIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjREMEQwRkY1RjgwNDExRUE5OTY2RDgxODY3NkJFODMxIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NEQwRDBGRjJGODA0MTFFQTk5NjZEODE4Njc2QkU4MzEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NEQwRDBGRjNGODA0MTFFQTk5NjZEODE4Njc2QkU4MzEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7/7gAOQWRvYmUAZMAAAAAB/9sAhAAGBAQEBQQGBQUGCQYFBgkLCAYGCAsMCgoLCgoMEAwMDAwMDBAMDg8QDw4MExMUFBMTHBsbGxwfHx8fHx8fHx8fAQcHBw0MDRgQEBgaFREVGh8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx//wAARCADIAMgDAREAAhEBAxEB/8QAcQABAQEAAwEBAAAAAAAAAAAAAAUEAQMGAgcBAQAAAAAAAAAAAAAAAAAAAAAQAAIBAwICBgkDBQAAAAAAAAABAhEDBCEFMVFBYXGREiKBscHRMkJSEyOh4XLxYjNDFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8A/fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHbHFyZ/Dam+yLA+Z2L0Pjtyj2poD4AAAAAAAAAAAAAAAAAAAAAAAAKWFs9y6lcvvwQeqj8z9wFaziY1n/HbUX9XF97A7QAGXI23EvJ1goyfzR0YEfN269jeZ+a03pNe0DIAAAAAAAAAAAAAAAAAAAACvtO3RcVkXlWutuL9YFYAAAAAOJRjKLjJVi9GmB5/csH/mu1h/in8PU+QGMAAAAAAAAAAAAAAAAAAaMDG/6MmMH8C80+xAelSSVFolwQAAAAAAAHVlWI37ErUulaPk+hgeYnCUJuElSUXRrrQHAAAAAAAAAAAAAAAAABa2Oz4bM7r4zdF2ICmAAAAAAAAAg7zZ8GX41wuJP0rRgYAAAAAAAAAAAAAAAAAD0m2R8ODaXU33tsDSAAAAAAAAAlb9HyWZcnJd9PcBHAAAAAAAAAAAAAAAAAPS7e64Vn+KA0AAAAAAAAAJm+v8Ftf3ewCKAAAAAAAAAAAAAAAAAX9muqeGo9NttP06+0DcAAAAAAAAAjb7dTu2ra+VOT9P8AQCWAAAAAAAAAAAAAAAAAUNmyPt5Ltv4bui/kuAF0AAAAAAADiUlGLlJ0SVW+oDzOXfd/Ind6JPRdS0QHSAAAAAAAAAAAAAAAAAE2nVaNcGB6Lbs6OTao9LsF51z60BrAAAAAABJ3jOVHjW3r/sa9QEgAAAAAAAAAAAAAAAAAAAPu1duWriuW34ZR4MC9hbnZyEoy8l36XwfYBsAAADaSq9EuLAlZ+7xSdrGdW9Hc5dgEdtt1erfFgAAAAAAAAAAAAAAAAADVjbblX6NR8MH80tEBRs7HYivyzlN8lovaBPzduvY0m6eK10TXtAyAarO55lpJK54orolr+4GqO/Xaea1FvqbXvA+Z77kNeW3GPbV+4DJfzcm/pcm3H6Vou5AdAFLC2ed2Pjv1txa8sV8T6wOL+yZEKu1JXFy4MDBOE4ScZxcZLinoB8gAAAAAAAAAAAB242LeyJ+C3GvN9C7QLmJtePYpKS+5c+p8F2IDYAANJqj1T4oCfk7Nj3G5Wn9qXJax7gJ93Z82D8sVNc4v30A6Xg5i42Z+iLfqARwcyT0sz9MWvWBps7LlTf5Grce9/oBTxdtxseklHxT+uWr9AGoAB138ezfj4bsFJdD6V2MCPm7RdtJzs1uW1xXzL3gTgAAAAAAAAADRhYc8q74I6RWs5ckB6GxYtWLat21SK731sDsAAAAAAAAAAAAAAAASt021NO/YjrxuQXT1oCOAAAAAAABzGLlJRSq26JAelwsWONYjbXxcZvmwO8AAAAAAAAAAAAAAAAAAef3TEWPkVivx3NY9T6UBiAAAAAABo2+VmGXblddIJ8eivRUD0oAAAAAAAAAAAAAAAAAAAYt4tKeFKVNYNSXfRgefAAAAAAAAr7VuSSWPedKaW5v1MCsAAAAAAAAAAAAAAAAAAIe6bj96Ts2n+JPzSXzP3ATgAAAAAAAAFbbt1UUrOQ9FpC4/UwK6aaqtU+DAAAAAAAAAAAAAAA4lKMIuUmoxWrb4ARNx3R3q2rLpa4Sl0y/YCcAAAAAAAAAAANmFud7G8r89r6X0dgFvGzLGRGtuWvTF6NAdwAAAAAAAAAAAy5W442PVN+K59EePp5ARMvOv5MvO6QXCC4AZwAAAAAAAAAAAAAcxlKLUotprg1owN+PvORborq+7Hnwl3gUbO74VzRydt8pKn68ANcJwmqwkpLmnUDkAAAAfNy9atqtyagut0AxXt5xIV8Fbj6lRd7Am5G65V6qUvtwfyx94GMAAAAAAAAAAAAAAAAAAAOU2nVOj5gdsc3LiqRvTpyqwOxbnnrhdfpSfrQB7pnv/AGvuS9gHXPMy5/Fem1yq0v0A6W29XqwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//Z";
/**
* Avatar 头像
* @description 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。
* @tutorial https://www.uvui.cn/components/avatar.html
*
* @property {String} src 头像路径,如加载失败,将会显示默认头像(不能为相对路径)
* @property {String} shape 头像形状 circle (默认) | square
* @property {String | Number} size 头像尺寸,可以为指定字符串(large, default, mini),或者数值 (默认 40
* @property {String} mode 头像图片的裁剪类型与uni的image组件的mode参数一致如效果达不到需求可尝试传widthFix值 (默认 'scaleToFill'
* @property {String} text 用文字替代图片级别优先于src
* @property {String} bgColor 背景颜色,一般显示文字时用 (默认 '#c0c4cc'
* @property {String} color 文字颜色 (默认 '#ffffff'
* @property {String | Number} fontSize 文字大小 (默认 18
* @property {String} icon 显示的图标
* @property {Boolean} mpAvatar 显示小程序头像只对百度微信QQ小程序有效 (默认 false
* @property {Boolean} randomBgColor 是否使用随机背景色 (默认 false
* @property {String} defaultUrl 加载失败的默认头像(组件有内置默认图片)
* @property {String | Number} colorIndex 如果配置了randomBgColor为true且配置了此值则从默认的背景色数组中取出对应索引的颜色值取值0-19之间
* @property {String} name 组件标识符 (默认 'level'
* @property {Object} customStyle 定义需要用到的外部样式
*
* @event {Function} click 点击组件时触发 index: 用户传递的标识符
* @example <uv-avatar :src="src" mode="square"></uv-avatar>
*/
export default {
name: 'uv-avatar',
emits: ['click'],
mixins: [mpMixin, mixin, props],
data() {
return {
// 如果配置randomBgColor参数为true在图标或者文字的模式下会随机从中取出一个颜色值当做背景色
colors: ['#ffb34b', '#f2bba9', '#f7a196', '#f18080', '#88a867', '#bfbf39', '#89c152', '#94d554', '#f19ec2',
'#afaae4', '#e1b0df', '#c38cc1', '#72dcdc', '#9acdcb', '#77b1cc', '#448aca', '#86cefa', '#98d1ee',
'#73d1f1',
'#80a7dc'
],
avatarUrl: this.src,
allowMp: false
}
},
watch: {
// 监听头像src的变化赋值给内部的avatarUrl变量因为图片加载失败时需要修改图片的src为默认值
// 而组件内部不能直接修改props的值所以需要一个中间变量
src: {
immediate: true,
handler(newVal) {
this.avatarUrl = newVal
// 如果没有传src则主动触发error事件用于显示默认的头像否则src为''空字符等的时候,会无内容展示
if(!newVal) {
this.errorHandler()
}
}
}
},
computed: {
imageStyle() {
const style = {}
return style
}
},
created() {
this.init()
},
methods: {
init() {
// 目前只有这几个小程序平台具有open-data标签
// 其他平台可以通过uni.getUserInfo类似接口获取信息但是需要弹窗授权(首次),不合符组件逻辑
// 故目前自动获取小程序头像只支持这几个平台
// #ifdef MP-WEIXIN || MP-QQ || MP-BAIDU
this.allowMp = true
// #endif
},
// 判断传入的name属性是否图片路径只要带有"/"均认为是图片形式
isImg() {
return this.src.indexOf('/') !== -1
},
// 图片加载时失败时触发
errorHandler() {
this.avatarUrl = this.defaultUrl || base64Avatar
},
clickHandler() {
this.$emit('click', this.name)
}
}
}
</script>
<style lang="scss" scoped>
@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
.uv-avatar {
@include flex;
align-items: center;
justify-content: center;
&--circle {
border-radius: 100px;
}
&--square {
border-radius: 4px;
}
&__image {
&--circle {
border-radius: 100px;
}
&--square {
border-radius: 4px;
}
}
}
</style>

View File

@@ -0,0 +1,89 @@
{
"id": "uv-avatar",
"displayName": "uv-avatar 头像 全面兼容小程序、nvue、vue2、vue3等多端",
"version": "1.0.5",
"description": "uv-avatar 本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。",
"keywords": [
"uv-avatar",
"uvui",
"uv-ui",
"avatar",
"头像"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [
"uv-ui-tools",
"uv-icon",
"uv-text"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
## Avatar 头像
> **组件名uv-avatar**
本组件一般用于展示头像的地方,如个人中心,或者评论列表页的用户头像展示等场所。
### <a href="https://www.uvui.cn/components/avatar.html" target="_blank">查看文档</a>
### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
#### 如使用过程中有任何问题或者您对uv-ui有一些好的建议欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>

View File

@@ -0,0 +1,8 @@
## 1.0.22023-07-03
1. 优化插槽自定义内容部分
2. 增加backToTop方法说明
## 1.0.12023-05-16
1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
2. 优化部分功能
## 1.0.02023-05-10
uv-back-top 返回顶部

View File

@@ -0,0 +1,58 @@
export default {
props: {
// 返回顶部的形状circle-圆形square-方形
mode: {
type: String,
default: 'circle'
},
// 自定义图标
icon: {
type: String,
default: 'arrow-upward'
},
// 提示文字
text: {
type: String,
default: ''
},
// 返回顶部滚动时间
duration: {
type: [String, Number],
default: 100
},
// 滚动距离
scrollTop: {
type: [String, Number],
default: 0
},
// 距离顶部多少距离显示单位px
top: {
type: [String, Number],
default: 400
},
// 返回顶部按钮到底部的距离单位px
bottom: {
type: [String, Number],
default: 100
},
// 返回顶部按钮到右边的距离单位px
right: {
type: [String, Number],
default: 20
},
// 层级
zIndex: {
type: [String, Number],
default: 9
},
// 图标的样式,对象形式
iconStyle: {
type: Object,
default: () => ({
color: '#909399',
fontSize: '19px'
})
},
...uni.$uv?.props?.backtop
}
}

View File

@@ -0,0 +1,116 @@
<template>
<uv-transition mode="fade" :customStyle="backTopStyle" :show="show">
<slot>
<view class="uv-back-top" :style="[contentStyle]" @click="backToTop">
<uv-icon :name="icon" :custom-style="iconStyle"></uv-icon>
<text v-if="text" class="uv-back-top__text">{{text}}</text>
</view>
</slot>
</uv-transition>
</template>
<script>
import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
import props from './props.js';
// #ifdef APP-NVUE
const dom = weex.requireModule('dom')
// #endif
/**
* backTop 返回顶部
* @description 本组件一个用于长页面,滑动一定距离后,出现返回顶部按钮,方便快速返回顶部的场景。
* @tutorial https://www.uvui.cn/components/backTop.html
* @property {String} mode 返回顶部的形状circle-圆形square-方形 (默认 'circle'
* @property {String} icon 自定义图标 (默认 'arrow-upward' 见官方文档示例
* @property {String} text 提示文字
* @property {String | Number} duration 返回顶部滚动时间 (默认 100
* @property {String | Number} scrollTop 滚动距离 (默认 0
* @property {String | Number} top 距离顶部多少距离显示单位px (默认 400
* @property {String | Number} bottom 返回顶部按钮到底部的距离单位px (默认 100
* @property {String | Number} right 返回顶部按钮到右边的距离单位px (默认 20
* @property {String | Number} zIndex 层级 (默认 9
* @property {Object<Object>} iconStyle 图标的样式,对象形式 (默认 {color: '#909399',fontSize: '19px'}
* @property {Object} customStyle 定义需要用到的外部样式
*
* @example <uv-back-top :scrollTop="scrollTop"></uv-back-top>
*/
export default {
name: 'uv-back-top',
emits: ['click'],
mixins: [mpMixin, mixin, props],
computed: {
backTopStyle() {
// 动画组件样式
const style = {
bottom: this.$uv.addUnit(this.bottom),
right: this.$uv.addUnit(this.right),
width: '40px',
height: '40px',
position: 'fixed',
zIndex: 10,
}
return style
},
show() {
return this.$uv.getPx(this.scrollTop) > this.$uv.getPx(this.top)
},
contentStyle() {
const style = {}
let radius = 0
// 是否圆形
if (this.mode === 'circle') {
radius = '100px'
} else {
radius = '4px'
}
// 为了兼容安卓nvue只能这么分开写
style.borderTopLeftRadius = radius
style.borderTopRightRadius = radius
style.borderBottomLeftRadius = radius
style.borderBottomRightRadius = radius
return this.$uv.deepMerge(style, this.$uv.addStyle(this.customStyle))
}
},
methods: {
backToTop() {
// #ifdef APP-NVUE
if (!this.$parent.$refs['uv-back-top']) {
this.$uv.error(`nvue页面需要给页面最外层元素设置"ref='uv-back-top'`)
}
dom.scrollToElement(this.$parent.$refs['uv-back-top'], {
offset: 0
})
// #endif
// #ifndef APP-NVUE
uni.pageScrollTo({
scrollTop: 0,
duration: this.duration
});
// #endif
this.$emit('click')
}
}
}
</script>
<style lang="scss" scoped>
@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
$uv-back-top-flex: 1 !default;
$uv-back-top-height: 100% !default;
$uv-back-top-background-color: #E1E1E1 !default;
$uv-back-top-tips-font-size: 12px !default;
.uv-back-top {
@include flex;
flex-direction: column;
align-items: center;
flex: $uv-back-top-flex;
height: $uv-back-top-height;
justify-content: center;
background-color: $uv-back-top-background-color;
&__tips {
font-size: $uv-back-top-tips-font-size;
transform: scale(0.8);
}
}
</style>

View File

@@ -0,0 +1,89 @@
{
"id": "uv-back-top",
"displayName": "uv-back-top 返回顶部 全面兼容小程序、nvue、vue2、vue3等多端",
"version": "1.0.2",
"description": "返回顶部 组件一个用于长页面,滑动一定距离后,出现返回顶部按钮,方便快速返回顶部的场景。",
"keywords": [
"uv-back-top",
"uvui",
"uv-ui",
"avatar",
"返回顶部"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [
"uv-ui-tools",
"uv-icon",
"uv-transition"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
## BackTop 返回顶部
> **组件名uv-back-top**
该组件一个用于长页面,滑动一定距离后,出现返回顶部按钮,方便快速返回顶部的场景。
### <a href="https://www.uvui.cn/components/backTop.html" target="_blank">查看文档</a>
### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
#### 如使用过程中有任何问题或者您对uv-ui有一些好的建议欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>

View File

@@ -0,0 +1,7 @@
## 1.0.22023-06-04
1. 修复type等属性为null的时候不显示徽标的BUG
## 1.0.12023-05-16
1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
2. 优化部分功能
## 1.0.02023-05-10
uv-badge 徽标数,数字角标

View File

@@ -0,0 +1,73 @@
export default {
props: {
// 是否显示圆点
isDot: {
type: Boolean,
default: false
},
// 显示的内容
value: {
type: [Number, String],
default: ''
},
// 是否显示
show: {
type: Boolean,
default: true
},
// 最大值,超过最大值会显示 '{max}+'
max: {
type: [Number, String],
default: 999
},
// 主题类型error|warning|success|primary
type: {
type: [String,undefined,null],
default: 'error'
},
// 当数值为 0 时,是否展示 Badge
showZero: {
type: Boolean,
default: false
},
// 背景颜色优先级比type高如设置type参数会失效
bgColor: {
type: [String, null],
default: null
},
// 字体颜色
color: {
type: [String, null],
default: null
},
// 徽标形状circle-四角均为圆角horn-左下角为直角
shape: {
type: [String,undefined,null],
default: 'circle'
},
// 设置数字的显示方式overflow|ellipsis|limit
// overflow会根据max字段判断超出显示`${max}+`
// ellipsis会根据max判断超出显示`${max}...`
// limit会依据1000作为判断条件超出1000显示`${value/1000}K`比如2.2k、3.34w最多保留2位小数
numberType: {
type: [String,undefined,null],
default: 'overflow'
},
// 设置badge的位置偏移格式为 [x, y]也即设置的为top和right的值absolute为true时有效
offset: {
type: Array,
default: () => []
},
// 是否反转背景和字体颜色
inverted: {
type: Boolean,
default: false
},
// 是否绝对定位
absolute: {
type: Boolean,
default: false
},
...uni.$uv?.props?.badge
}
}

View File

@@ -0,0 +1,176 @@
<template>
<text
v-if="show && ((Number(value) === 0 ? showZero : true) || isDot)"
:class="[isDot ? 'uv-badge--dot' : 'uv-badge--not-dot', inverted && 'uv-badge--inverted', shape === 'horn' && 'uv-badge--horn', `uv-badge--${propsType}${inverted ? '--inverted' : ''}`]"
:style="[$uv.addStyle(customStyle), badgeStyle]"
class="uv-badge"
>{{ isDot ? '' :showValue }}</text>
</template>
<script>
import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
import props from './props.js';
/**
* badge 徽标数
* @description 该组件一般用于图标右上角显示未读的消息数量,提示用户点击,有圆点和圆包含文字两种形式。
* @tutorial https://www.uvui.cn/components/badge.html
*
* @property {Boolean} isDot 是否显示圆点 (默认 false
* @property {String | Number} value 显示的内容
* @property {Boolean} show 是否显示 (默认 true
* @property {String | Number} max 最大值,超过最大值会显示 '{max}+' 默认999
* @property {String} type 主题类型error|warning|success|primary (默认 'error'
* @property {Boolean} showZero 当数值为 0 时,是否展示 Badge (默认 false
* @property {String} bgColor 背景颜色优先级比type高如设置type参数会失效
* @property {String} color 字体颜色 (默认 '#ffffff'
* @property {String} shape 徽标形状circle-四角均为圆角horn-左下角为直角 (默认 'circle'
* @property {String} numberType 设置数字的显示方式overflow|ellipsis|limit (默认 'overflow'
* @property {Array}} offset 设置badge的位置偏移格式为 [x, y]也即设置的为top和right的值absolute为true时有效
* @property {Boolean} inverted 是否反转背景和字体颜色(默认 false
* @property {Boolean} absolute 是否绝对定位(默认 false
* @property {Object} customStyle 定义需要用到的外部样式
* @example <uv-badge :type="type" :count="count"></uv-badge>
*/
export default {
name: 'uv-badge',
mixins: [mpMixin, mixin, props],
computed: {
// 是否将badge中心与父组件右上角重合
boxStyle() {
let style = {};
return style;
},
// 整个组件的样式
badgeStyle() {
const style = {}
if(this.color) {
style.color = this.color
}
if (this.bgColor && !this.inverted) {
style.backgroundColor = this.bgColor
}
if (this.absolute) {
style.position = 'absolute'
// 如果有设置offset参数
if(this.offset.length) {
// top和right分为为offset的第一个和第二个值如果没有第二个值则right等于top
const top = this.offset[0]
const right = this.offset[1] || top
style.top = this.$uv.addUnit(top)
style.right = this.$uv.addUnit(right)
}
}
return style
},
showValue() {
switch (this.numberType) {
case "overflow":
return Number(this.value) > Number(this.max) ? this.max + "+" : this.value
break;
case "ellipsis":
return Number(this.value) > Number(this.max) ? "..." : this.value
break;
case "limit":
return Number(this.value) > 999 ? Number(this.value) >= 9999 ?
Math.floor(this.value / 1e4 * 100) / 100 + "w" : Math.floor(this.value /
1e3 * 100) / 100 + "k" : this.value
break;
default:
return Number(this.value)
}
},
propsType(){
return this.type || 'error'
}
}
}
</script>
<style lang="scss" scoped>
@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
@import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
$uv-badge-primary: $uv-primary !default;
$uv-badge-error: $uv-error !default;
$uv-badge-success: $uv-success !default;
$uv-badge-info: $uv-info !default;
$uv-badge-warning: $uv-warning !default;
$uv-badge-dot-radius: 100px !default;
$uv-badge-dot-size: 8px !default;
$uv-badge-dot-right: 4px !default;
$uv-badge-dot-top: 0 !default;
$uv-badge-text-font-size: 11px !default;
$uv-badge-text-right: 10px !default;
$uv-badge-text-padding: 2px 5px !default;
$uv-badge-text-align: center !default;
$uv-badge-text-color: #FFFFFF !default;
.uv-badge {
border-top-right-radius: $uv-badge-dot-radius;
border-top-left-radius: $uv-badge-dot-radius;
border-bottom-left-radius: $uv-badge-dot-radius;
border-bottom-right-radius: $uv-badge-dot-radius;
@include flex;
line-height: $uv-badge-text-font-size;
text-align: $uv-badge-text-align;
font-size: $uv-badge-text-font-size;
color: $uv-badge-text-color;
&--dot {
height: $uv-badge-dot-size;
width: $uv-badge-dot-size;
}
&--inverted {
font-size: 13px;
}
&--not-dot {
padding: $uv-badge-text-padding;
}
&--horn {
border-bottom-left-radius: 0;
}
&--primary {
background-color: $uv-badge-primary;
}
&--primary--inverted {
color: $uv-badge-primary;
}
&--error {
background-color: $uv-badge-error;
}
&--error--inverted {
color: $uv-badge-error;
}
&--success {
background-color: $uv-badge-success;
}
&--success--inverted {
color: $uv-badge-success;
}
&--info {
background-color: $uv-badge-info;
}
&--info--inverted {
color: $uv-badge-info;
}
&--warning {
background-color: $uv-badge-warning;
}
&--warning--inverted {
color: $uv-badge-warning;
}
}
</style>

View File

@@ -0,0 +1,87 @@
{
"id": "uv-badge",
"displayName": "uv-badge 徽标数,数字角标 全面兼容小程序、nvue、vue2、vue3等多端",
"version": "1.0.2",
"description": "徽标数一般用于图标右上角显示未读的消息数量,提示用户点击,有圆点和圆包含文字两种形式。",
"keywords": [
"uv-badge",
"uvui",
"uv-ui",
"徽标数",
"数字角标"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [
"uv-ui-tools"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
## Badge 徽标数
> **组件名uv-badge**
该组件一般用于图标右上角显示未读的消息数量,提示用户点击,有圆点和圆包含文字两种形式。
### <a href="https://www.uvui.cn/components/badge.html" target="_blank">查看文档</a>
### [完整示例项目下载 | 关注更多组件](https://ext.dcloud.net.cn/plugin?name=uv-ui)
#### 如使用过程中有任何问题或者您对uv-ui有一些好的建议欢迎加入 uv-ui 交流群:<a href="https://ext.dcloud.net.cn/plugin?id=12287" target="_blank">uv-ui</a>、<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>

View File

@@ -0,0 +1,33 @@
## 1.0.152023-12-20
1. 优化
## 1.0.142023-12-06
1. 优化
## 1.0.132023-12-06
1. 阻止事件冒泡处理
## 1.0.122023-10-19
1. 增加后置插槽
## 1.0.112023-09-21
1. 修复通过customStyle修改按钮宽度组件中最外层节点不改变的问题
## 1.0.102023-09-15
1. 按钮支持open-type="agreePrivacyAuthorization"
## 1.0.92023-09-11
1. 增加参数iconSize用于控制图标的大小
## 1.0.82023-09-10
1. 修复多个按钮在一行宽度不正常的BUG
## 1.0.72023-09-07
1. 修复warning颜色对应错误的BUG
## 1.0.62023-07-25
1. 增加customTextStyle属性方便自定义文字样式
## 1.0.52023-07-20
1. 解决微信小程序动态设置hover-class点击态不消失的BUG
## 1.0.42023-06-29
1. 修改上次更新出现nvue报错异常
## 1.0.32023-06-28
修复设置open-type="chooseAvatar"等值不生效的BUG
## 1.0.22023-06-01
1. 修复按钮点击触发两次的BUG
## 1.0.12023-05-16
1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
2. 优化部分功能
## 1.0.02023-05-10
uv-button 按钮

View File

@@ -0,0 +1,46 @@
$uv-button-active-opacity:0.75 !default;
$uv-button-loading-text-margin-left:4px !default;
$uv-button-text-color: #FFFFFF !default;
$uv-button-text-plain-error-color:$uv-error !default;
$uv-button-text-plain-warning-color:$uv-warning !default;
$uv-button-text-plain-success-color:$uv-success !default;
$uv-button-text-plain-info-color:$uv-info !default;
$uv-button-text-plain-primary-color:$uv-primary !default;
.uv-button {
&--active {
opacity: $uv-button-active-opacity;
}
&--active--plain {
background-color: rgb(217, 217, 217);
}
&__loading-text {
margin-left:$uv-button-loading-text-margin-left;
}
&__text,
&__loading-text {
color:$uv-button-text-color;
}
&__text--plain--error {
color:$uv-button-text-plain-error-color;
}
&__text--plain--warning {
color:$uv-button-text-plain-warning-color;
}
&__text--plain--success{
color:$uv-button-text-plain-success-color;
}
&__text--plain--info {
color:$uv-button-text-plain-info-color;
}
&__text--plain--primary {
color:$uv-button-text-plain-primary-color;
}
}

View File

@@ -0,0 +1,163 @@
export default {
props: {
// 是否细边框
hairline: {
type: Boolean,
default: true
},
// 按钮的预置样式infoprimaryerrorwarningsuccess
type: {
type: String,
default: 'info'
},
// 按钮尺寸largenormalsmallmini
size: {
type: String,
default: 'normal'
},
// 按钮形状circle两边为半圆square带圆角
shape: {
type: String,
default: 'square'
},
// 按钮是否镂空
plain: {
type: Boolean,
default: false
},
// 是否禁止状态
disabled: {
type: Boolean,
default: false
},
// 是否加载中
loading: {
type: Boolean,
default: false
},
// 加载中提示文字
loadingText: {
type: [String, Number],
default: ''
},
// 加载状态图标类型
loadingMode: {
type: String,
default: 'spinner'
},
// 加载图标大小
loadingSize: {
type: [String, Number],
default: 14
},
// 开放能力具体请看uniapp稳定关于button组件部分说明
// https://uniapp.dcloud.io/component/button
openType: {
type: String,
default: ''
},
// 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件
// 取值为submit提交表单reset重置表单
formType: {
type: String,
default: ''
},
// 打开 APP 时,向 APP 传递的参数open-type=launchApp时有效
// 只微信小程序、QQ小程序有效
appParameter: {
type: String,
default: ''
},
// 指定是否阻止本节点的祖先节点出现点击态,微信小程序有效
hoverStopPropagation: {
type: Boolean,
default: true
},
// 指定返回用户信息的语言zh_CN 简体中文zh_TW 繁体中文en 英文。只微信小程序有效
lang: {
type: String,
default: 'en'
},
// 会话来源open-type="contact"时有效。只微信小程序有效
sessionFrom: {
type: String,
default: ''
},
// 会话内消息卡片标题open-type="contact"时有效
// 默认当前标题,只微信小程序有效
sendMessageTitle: {
type: String,
default: ''
},
// 会话内消息卡片点击跳转小程序路径open-type="contact"时有效
// 默认当前分享路径,只微信小程序有效
sendMessagePath: {
type: String,
default: ''
},
// 会话内消息卡片图片open-type="contact"时有效
// 默认当前页面截图,只微信小程序有效
sendMessageImg: {
type: String,
default: ''
},
// 是否显示会话内消息卡片,设置此参数为 true用户进入客服会话会在右下角显示"可能要发送的小程序"提示,
// 用户点击后可以快速发送小程序消息open-type="contact"时有效
showMessageCard: {
type: Boolean,
default: true
},
// 额外传参参数用于小程序的data-xxx属性通过target.dataset.name获取
dataName: {
type: String,
default: ''
},
// 节流,一定时间内只能触发一次
throttleTime: {
type: [String, Number],
default: 0
},
// 按住后多久出现点击态,单位毫秒
hoverStartTime: {
type: [String, Number],
default: 0
},
// 手指松开后点击态保留时间,单位毫秒
hoverStayTime: {
type: [String, Number],
default: 200
},
// 按钮文字之所以通过props传入是因为slot传入的话
// nvue中无法控制文字的样式
text: {
type: [String, Number],
default: ''
},
// 按钮图标
icon: {
type: String,
default: ''
},
// 按钮图标大小
iconSize: {
type: [String, Number],
default: ''
},
// 按钮图标颜色
iconColor: {
type: String,
default: '#000000'
},
// 按钮颜色支持传入linear-gradient渐变色
color: {
type: String,
default: ''
},
// 自定义按钮文本样式
customTextStyle: {
type: [Object,String],
default: ''
},
...uni.$uv?.props?.button
}
}

View File

@@ -0,0 +1,528 @@
<template>
<view
class="uv-button-wrapper"
:style="[btnWrapperStyle]"
>
<!-- #ifndef APP-NVUE -->
<!-- #ifdef MP -->
<!-- 为了解决微信小程序动态设置hover-class点击态不消失的BUG -->
<view class="uv-button-wrapper--dis" v-if="disabled || loading"></view>
<button
:hover-start-time="Number(hoverStartTime)"
:hover-stay-time="Number(hoverStayTime)"
:form-type="formType"
:open-type="openType"
:app-parameter="appParameter"
:hover-stop-propagation="hoverStopPropagation"
:send-message-title="sendMessageTitle"
:send-message-path="sendMessagePath"
:lang="lang"
:data-name="dataName"
:session-from="sessionFrom"
:send-message-img="sendMessageImg"
:show-message-card="showMessageCard"
@getphonenumber="onGetPhoneNumber"
@getuserinfo="onGetUserInfo"
@error="onError"
@opensetting="onOpenSetting"
@launchapp="onLaunchApp"
@contact="onContact"
@chooseavatar="onChooseavatar"
@agreeprivacyauthorization="onAgreeprivacyauthorization"
@addgroupapp="onAddgroupapp"
@chooseaddress="onChooseaddress"
@subscribe="onSubscribe"
@login="onLogin"
@im="onIm"
hover-class="uv-button--active"
class="uv-button uv-reset-button"
:style="[baseColor, $uv.addStyle(customStyle)]"
@tap="clickHandler"
:class="bemClass"
>
<!-- #endif -->
<!-- #ifndef MP -->
<button
:hover-start-time="Number(hoverStartTime)"
:hover-stay-time="Number(hoverStayTime)"
:form-type="formType"
:open-type="openType"
:app-parameter="appParameter"
:hover-stop-propagation="hoverStopPropagation"
:send-message-title="sendMessageTitle"
:send-message-path="sendMessagePath"
:lang="lang"
:data-name="dataName"
:session-from="sessionFrom"
:send-message-img="sendMessageImg"
:show-message-card="showMessageCard"
:hover-class="!disabled && !loading ? 'uv-button--active' : ''"
class="uv-button uv-reset-button"
:style="[baseColor, $uv.addStyle(customStyle)]"
@tap="clickHandler"
:class="bemClass"
>
<!-- #endif -->
<template v-if="loading">
<uv-loading-icon
:mode="loadingMode"
:size="loadingSize * 1.15"
:color="loadingColor"
></uv-loading-icon>
<text
class="uv-button__loading-text"
:style="[
{ fontSize: textSize + 'px' },
$uv.addStyle(customTextStyle)
]"
>{{ loadingText || text }}</text>
</template>
<template v-else>
<uv-icon
v-if="icon"
:name="icon"
:color="iconColorCom"
:size="getIconSize"
:customStyle="{ marginRight: '2px' }"
></uv-icon>
<slot>
<text
class="uv-button__text"
:style="[
{ fontSize: textSize + 'px' },
$uv.addStyle(customTextStyle)
]"
>{{ text }}</text>
</slot>
<slot name="suffix"></slot>
</template>
</button>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<view
:hover-start-time="Number(hoverStartTime)"
:hover-stay-time="Number(hoverStayTime)"
class="uv-button"
:hover-class="
!disabled && !loading && !color && (plain || type === 'info')
? 'uv-button--active--plain'
: !disabled && !loading && !plain
? 'uv-button--active'
: ''
"
@tap="clickHandler"
:class="bemClass"
:style="[baseColor, $uv.addStyle(customStyle)]"
>
<template v-if="loading">
<uv-loading-icon
:mode="loadingMode"
:size="loadingSize * 1.15"
:color="loadingColor"
></uv-loading-icon>
<text
class="uv-button__loading-text"
:style="[nvueTextStyle,$uv.addStyle(customTextStyle)]"
:class="[plain && `uv-button__text--plain--${type}`]"
>{{ loadingText || text }}</text>
</template>
<template v-else>
<uv-icon
v-if="icon"
:name="icon"
:color="iconColorCom"
:size="getIconSize"
></uv-icon>
<text
class="uv-button__text"
:style="[
{
marginLeft: icon ? '2px' : 0,
},
nvueTextStyle,
$uv.addStyle(customTextStyle)
]"
:class="[plain && `uv-button__text--plain--${type}`]"
>{{ text }}</text>
<slot name="suffix"></slot>
</template>
</view>
<!-- #endif -->
</view>
</template>
<script>
import throttle from '@/uni_modules/uv-ui-tools/libs/function/throttle.js';
import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js'
import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js'
import button from '@/uni_modules/uv-ui-tools/libs/mixin/button.js'
import openType from '@/uni_modules/uv-ui-tools/libs/mixin/openType.js'
import props from "./props.js";
/**
* button 按钮
* @description Button 按钮
* @tutorial https://www.uvui.cn/components/button.html
* @property {Boolean} hairline 是否显示按钮的细边框 (默认 true )
* @property {String} type 按钮的预置样式infoprimaryerrorwarningsuccess (默认 'info' )
* @property {String} size 按钮尺寸largenormalmini (默认 normal
* @property {String} shape 按钮形状circle两边为半圆square带圆角 (默认 'square'
* @property {Boolean} plain 按钮是否镂空,背景色透明 (默认 false
* @property {Boolean} disabled 是否禁用 (默认 false
* @property {Boolean} loading 按钮名称前是否带 loading 图标(App-nvue 平台,在 ios 上为雪花Android上为圆圈) (默认 false
* @property {String | Number} loadingText 加载中提示文字
* @property {String} loadingMode 加载状态图标类型 (默认 'spinner'
* @property {String | Number} loadingSize 加载图标大小 (默认 15
* @property {String} openType 开放能力具体请看uniapp稳定关于button组件部分说明
* @property {String} formType 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件
* @property {String} appParameter 打开 APP 时,向 APP 传递的参数open-type=launchApp时有效 只微信小程序、QQ小程序有效
* @property {Boolean} hoverStopPropagation 指定是否阻止本节点的祖先节点出现点击态,微信小程序有效(默认 true
* @property {String} lang 指定返回用户信息的语言zh_CN 简体中文zh_TW 繁体中文en 英文(默认 en
* @property {String} sessionFrom 会话来源openType="contact"时有效
* @property {String} sendMessageTitle 会话内消息卡片标题openType="contact"时有效
* @property {String} sendMessagePath 会话内消息卡片点击跳转小程序路径openType="contact"时有效
* @property {String} sendMessageImg 会话内消息卡片图片openType="contact"时有效
* @property {Boolean} showMessageCard 是否显示会话内消息卡片,设置此参数为 true用户进入客服会话会在右下角显示"可能要发送的小程序"提示用户点击后可以快速发送小程序消息openType="contact"时有效默认false
* @property {String} dataName 额外传参参数用于小程序的data-xxx属性通过target.dataset.name获取
* @property {String | Number} throttleTime 节流,一定时间内只能触发一次 (默认 0 )
* @property {String | Number} hoverStartTime 按住后多久出现点击态,单位毫秒 (默认 0 )
* @property {String | Number} hoverStayTime 手指松开后点击态保留时间,单位毫秒 (默认 200 )
* @property {String | Number} text 按钮文字之所以通过props传入是因为slot传入的话nvue中无法控制文字的样式
* @property {String} icon 按钮图标
* @property {String} iconColor 按钮图标颜色
* @property {String} color 按钮颜色支持传入linear-gradient渐变色
* @property {Object} customStyle 定义需要用到的外部样式
* @event {Function} click 非禁止并且非加载中,才能点击
* @event {Function} getphonenumber open-type="getPhoneNumber"时有效
* @event {Function} getuserinfo 用户点击该按钮时会返回获取到的用户信息从返回参数的detail中获取到的值同uni.getUserInfo
* @event {Function} error 当使用开放能力时,发生错误的回调
* @event {Function} opensetting 在打开授权设置页并关闭后回调
* @event {Function} launchapp 打开 APP 成功的回调
* @example <uv-button>月落</uv-button>
*/
export default {
name: "uv-button",
// #ifdef MP
mixins: [mpMixin, mixin, button, openType, props],
// #endif
// #ifndef MP
mixins: [mpMixin, mixin, props],
// #endif
emits: ['click'],
data() {
return {};
},
computed: {
// 生成bem风格的类名
bemClass() {
// this.bem为一个computed变量在mixin中
if (!this.color) {
return this.bem("button",
["type", "shape", "size"],
["disabled", "plain", "hairline"]);
} else {
// 由于nvue的原因在有color参数时不需要传入type否则会生成type相关的类型影响最终的样式
return this.bem("button",
["shape", "size"],
["disabled", "plain", "hairline"]);
}
},
loadingColor() {
if (this.plain) {
// 如果有设置color值则用color值否则使用type主题颜色
return this.color ? this.color : '#3c9cff';
}
if (this.type === "info") {
return "#c9c9c9";
}
return "rgb(200, 200, 200)";
},
iconColorCom() {
// 如果是镂空状态设置了color就用color值否则使用主题颜色
// uv-icon的color能接受一个主题颜色的值
if (this.iconColor) return this.iconColor;
if (this.plain) {
return this.color ? this.color : this.type;
} else {
return this.type === "info" ? "#000000" : "#ffffff";
}
},
baseColor() {
let style = {};
if (this.color) {
// 针对自定义了color颜色的情况镂空状态下就是用自定义的颜色
style.color = this.plain ? this.color : "white";
if (!this.plain) {
// 非镂空,背景色使用自定义的颜色
style["background-color"] = this.color;
}
if (this.color.indexOf("gradient") !== -1) {
// 如果自定义的颜色为渐变色不显示边框以及通过backgroundImage设置渐变色
// weex文档说明可以写borderWidth的形式为什么这里需要分开写
// 因为weex是阿里巴巴为了部门业绩考核而做的你懂的东西所以需要这么写才有效
style.borderTopWidth = 0;
style.borderRightWidth = 0;
style.borderBottomWidth = 0;
style.borderLeftWidth = 0;
if (!this.plain) {
style.backgroundImage = this.color;
}
} else {
// 非渐变色,则设置边框相关的属性
style.borderColor = this.color;
style.borderWidth = "1px";
style.borderStyle = "solid";
}
}
return style;
},
// nvue版本按钮的字体不会继承父组件的颜色需要对每一个text组件进行单独的设置
nvueTextStyle() {
let style = {};
// 针对自定义了color颜色的情况镂空状态下就是用自定义的颜色
if (this.type === "info") {
style.color = "#323233";
}
if (this.color) {
style.color = this.plain ? this.color : "white";
}
style.fontSize = this.textSize + "px";
return style;
},
// 字体大小
textSize() {
let fontSize = 14,
{ size } = this;
if (size === "large") fontSize = 16;
if (size === "normal") fontSize = 14;
if (size === "small") fontSize = 12;
if (size === "mini") fontSize = 10;
return fontSize;
},
// 设置图标大小
getIconSize() {
const size = this.iconSize ? this.iconSize : this.textSize * 1.35;
return this.$uv.addUnit(size);
},
// 设置外层盒子的宽度,其他样式不需要
btnWrapperStyle() {
const style = {};
const customStyle = this.$uv.addStyle(this.customStyle);
if(customStyle.width) style.width = customStyle.width;
return style;
}
},
methods: {
clickHandler() {
// 非禁止并且非加载中,才能点击
if (!this.disabled && !this.loading) {
// 进行节流控制每this.throttle毫秒内只在开始处执行
throttle(() => {
this.$emit("click");
}, this.throttleTime);
}
}
}
}
</script>
<style lang="scss" scoped>
$show-reset-button: 1;
@import '@/uni_modules/uv-ui-tools/libs/css/variable.scss';
@import '@/uni_modules/uv-ui-tools/libs/css/components.scss';
@import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
/* #ifndef APP-NVUE */
@import "./vue.scss";
/* #endif */
/* #ifdef APP-NVUE */
@import "./nvue.scss";
/* #endif */
$uv-button-uv-button-height: 40px !default;
$uv-button-text-font-size: 15px !default;
$uv-button-loading-text-font-size: 15px !default;
$uv-button-loading-text-margin-left: 4px !default;
$uv-button-large-width: 100% !default;
$uv-button-large-height: 50px !default;
$uv-button-normal-padding: 0 12px !default;
$uv-button-large-padding: 0 15px !default;
$uv-button-normal-font-size: 14px !default;
$uv-button-small-min-width: 60px !default;
$uv-button-small-height: 30px !default;
$uv-button-small-padding: 0px 8px !default;
$uv-button-mini-padding: 0px 8px !default;
$uv-button-small-font-size: 12px !default;
$uv-button-mini-height: 22px !default;
$uv-button-mini-font-size: 10px !default;
$uv-button-mini-min-width: 50px !default;
$uv-button-disabled-opacity: 0.5 !default;
$uv-button-info-color: #323233 !default;
$uv-button-info-background-color: #fff !default;
$uv-button-info-border-color: #ebedf0 !default;
$uv-button-info-border-width: 1px !default;
$uv-button-info-border-style: solid !default;
$uv-button-success-color: #fff !default;
$uv-button-success-background-color: $uv-success !default;
$uv-button-success-border-color: $uv-button-success-background-color !default;
$uv-button-success-border-width: 1px !default;
$uv-button-success-border-style: solid !default;
$uv-button-primary-color: #fff !default;
$uv-button-primary-background-color: $uv-primary !default;
$uv-button-primary-border-color: $uv-button-primary-background-color !default;
$uv-button-primary-border-width: 1px !default;
$uv-button-primary-border-style: solid !default;
$uv-button-error-color: #fff !default;
$uv-button-error-background-color: $uv-error !default;
$uv-button-error-border-color: $uv-button-error-background-color !default;
$uv-button-error-border-width: 1px !default;
$uv-button-error-border-style: solid !default;
$uv-button-warning-color: #fff !default;
$uv-button-warning-background-color: $uv-warning !default;
$uv-button-warning-border-color: $uv-button-warning-background-color !default;
$uv-button-warning-border-width: 1px !default;
$uv-button-warning-border-style: solid !default;
$uv-button-block-width: 100% !default;
$uv-button-circle-border-top-right-radius: 100px !default;
$uv-button-circle-border-top-left-radius: 100px !default;
$uv-button-circle-border-bottom-left-radius: 100px !default;
$uv-button-circle-border-bottom-right-radius: 100px !default;
$uv-button-square-border-top-right-radius: 3px !default;
$uv-button-square-border-top-left-radius: 3px !default;
$uv-button-square-border-bottom-left-radius: 3px !default;
$uv-button-square-border-bottom-right-radius: 3px !default;
$uv-button-icon-min-width: 1em !default;
$uv-button-plain-background-color: #fff !default;
$uv-button-hairline-border-width: 0.5px !default;
.uv-button {
height: $uv-button-uv-button-height;
position: relative;
align-items: center;
justify-content: center;
@include flex;
/* #ifndef APP-NVUE */
box-sizing: border-box;
/* #endif */
flex-direction: row;
&__text {
font-size: $uv-button-text-font-size;
}
&__loading-text {
font-size: $uv-button-loading-text-font-size;
margin-left: $uv-button-loading-text-margin-left;
}
&--large {
/* #ifndef APP-NVUE */
width: $uv-button-large-width;
/* #endif */
height: $uv-button-large-height;
padding: $uv-button-large-padding;
}
&--normal {
padding: $uv-button-normal-padding;
font-size: $uv-button-normal-font-size;
}
&--small {
/* #ifndef APP-NVUE */
min-width: $uv-button-small-min-width;
/* #endif */
height: $uv-button-small-height;
padding: $uv-button-small-padding;
font-size: $uv-button-small-font-size;
}
&--mini {
height: $uv-button-mini-height;
font-size: $uv-button-mini-font-size;
/* #ifndef APP-NVUE */
min-width: $uv-button-mini-min-width;
/* #endif */
padding: $uv-button-mini-padding;
}
&--disabled {
opacity: $uv-button-disabled-opacity;
}
&--info {
color: $uv-button-info-color;
background-color: $uv-button-info-background-color;
border-color: $uv-button-info-border-color;
border-width: $uv-button-info-border-width;
border-style: $uv-button-info-border-style;
}
&--success {
color: $uv-button-success-color;
background-color: $uv-button-success-background-color;
border-color: $uv-button-success-border-color;
border-width: $uv-button-success-border-width;
border-style: $uv-button-success-border-style;
}
&--primary {
color: $uv-button-primary-color;
background-color: $uv-button-primary-background-color;
border-color: $uv-button-primary-border-color;
border-width: $uv-button-primary-border-width;
border-style: $uv-button-primary-border-style;
}
&--error {
color: $uv-button-error-color;
background-color: $uv-button-error-background-color;
border-color: $uv-button-error-border-color;
border-width: $uv-button-error-border-width;
border-style: $uv-button-error-border-style;
}
&--warning {
color: $uv-button-warning-color;
background-color: $uv-button-warning-background-color;
border-color: $uv-button-warning-border-color;
border-width: $uv-button-warning-border-width;
border-style: $uv-button-warning-border-style;
}
&--block {
@include flex;
width: $uv-button-block-width;
}
&--circle {
border-top-right-radius: $uv-button-circle-border-top-right-radius;
border-top-left-radius: $uv-button-circle-border-top-left-radius;
border-bottom-left-radius: $uv-button-circle-border-bottom-left-radius;
border-bottom-right-radius: $uv-button-circle-border-bottom-right-radius;
}
&--square {
border-bottom-left-radius: $uv-button-square-border-top-right-radius;
border-bottom-right-radius: $uv-button-square-border-top-left-radius;
border-top-left-radius: $uv-button-square-border-bottom-left-radius;
border-top-right-radius: $uv-button-square-border-bottom-right-radius;
}
&__icon {
/* #ifndef APP-NVUE */
min-width: $uv-button-icon-min-width;
line-height: inherit !important;
vertical-align: top;
/* #endif */
}
&--plain {
background-color: $uv-button-plain-background-color;
}
&--hairline {
border-width: $uv-button-hairline-border-width !important;
}
}
</style>

View File

@@ -0,0 +1,93 @@
@import '@/uni_modules/uv-ui-tools/libs/css/color.scss';
// nvue下hover-class无效
$uv-button-before-top:50% !default;
$uv-button-before-left:50% !default;
$uv-button-before-width:100% !default;
$uv-button-before-height:100% !default;
$uv-button-before-transform:translate(-50%, -50%) !default;
$uv-button-before-opacity:0 !default;
$uv-button-before-background-color:#000 !default;
$uv-button-before-border-color:#000 !default;
$uv-button-active-before-opacity:.15 !default;
$uv-button-icon-margin-left:4px !default;
$uv-button-plain-uv-button-info-color:$uv-info;
$uv-button-plain-uv-button-success-color:$uv-success;
$uv-button-plain-uv-button-error-color:$uv-error;
$uv-button-plain-uv-button-warning-color:$uv-warning;
.uv-button-wrapper {
position: relative;
&--dis {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
z-index: 9;
}
}
.uv-button {
width: 100%;
&__text {
white-space: nowrap;
line-height: 1;
}
&:before {
position: absolute;
top:$uv-button-before-top;
left:$uv-button-before-left;
width:$uv-button-before-width;
height:$uv-button-before-height;
border: inherit;
border-radius: inherit;
transform:$uv-button-before-transform;
opacity:$uv-button-before-opacity;
content: " ";
background-color:$uv-button-before-background-color;
border-color:$uv-button-before-border-color;
}
&--active {
&:before {
opacity: .15
}
}
&__icon+&__text:not(:empty),
&__loading-text {
margin-left:$uv-button-icon-margin-left;
}
&--plain {
&.uv-button--primary {
color: $uv-primary;
}
}
&--plain {
&.uv-button--info {
color:$uv-button-plain-uv-button-info-color;
}
}
&--plain {
&.uv-button--success {
color:$uv-button-plain-uv-button-success-color;
}
}
&--plain {
&.uv-button--error {
color:$uv-button-plain-uv-button-error-color;
}
}
&--plain {
&.uv-button--warning {
color:$uv-button-plain-uv-button-warning-color;
}
}
}

View File

@@ -0,0 +1,89 @@
{
"id": "uv-button",
"displayName": "uv-button 按钮 全面兼容vue3+2、app、h5、小程序等多端",
"version": "1.0.15",
"description": "按钮组件内部实现以uni-app的button组件为基础进行二次封装灵活配置功能齐全兼容全端。",
"keywords": [
"uv-button",
"uvui",
"uv-ui",
"button",
"按钮"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [
"uv-ui-tools",
"uv-loading-icon",
"uv-icon"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

View File

@@ -0,0 +1,19 @@
## Button 按钮
> **组件名uv-button**
该组件内部实现以`uni-app``button`组件为基础,进行二次封装,灵活配置,功能齐全,兼容全端。灵活配置,内置状态设置,开箱即用。
# <a href="https://www.uvui.cn/components/button.html" target="_blank">查看文档</a>
## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui) <small>(请不要 下载插件ZIP</small>
### [更多插件请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
![image](https://mp-a667b617-c5f1-4a2d-9a54-683a67cff588.cdn.bspapp.com/uv-ui/banner.png)
</a>
#### 如使用过程中有任何问题反馈或者您对uv-ui有一些好的建议欢迎加入uv-ui官方交流群<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>

Some files were not shown because too many files have changed in this diff Show More