2025/2/8第一次更新

This commit is contained in:
爱吃咸鱼小猫咪
2025-02-08 18:50:38 +08:00
commit d7af560866
26519 changed files with 5046029 additions and 0 deletions
+73
View File
@@ -0,0 +1,73 @@
<script setup lang="ts">
import {provide,reactive} from 'vue'
import {onHide, onLoad, onShow} from "@dcloudio/uni-app";
import {useAppStore} from "@/stores/appStore.ts";
import type {SocketState} from "@/types";
onLoad(()=>{
console.log('App onLoad')
// on(EventType.AUTH_LOGOUT,()=>{
// console.log('退出登录事件')
// })
})
onShow(()=>{
console.log('App onShow')
})
onHide(()=>{
console.log('App onHide')
})
// 初始化store
useAppStore().init()
const socketState = reactive<SocketState>({ socket: null,isInitialized:false});
provide<SocketState>('socketState',socketState)
const {uniPlatform}=uni.getSystemInfoSync()
console.log('平台信息',uniPlatform)
</script>
<style lang="css">
/*每个页面公共css */
@import '@tuniao/tn-style/dist/uniapp/index.css';
:root {
/* --tn-color-white: #bf9d45 !important; !* 全局变量 *!
--tn-bg-color: #bf9d45 !important; !* 全局变量 *!
--tn-gray-light_bg: #bf9d45 !important; !* 全局变量 *!*/
--primary-color-base:#8ba2da;
--primary-color-light: #636cd4;
--primary-color: #4338CA;
}
body,page{
--tn-color-primary: #4338CA;
}
.container {
margin: 0 5rpx 0 5rpx;
background-color: #F6F7FA;
}
body{
background: #F6F7FA;
transition: background 1s ease-in-out; /* 背景颜色的过渡效果 */
height: 100vh; /* 确保背景覆盖整个页面 */
margin: 0; /* 去掉默认的外边距 */
}
</style>
<style lang="scss">
/* 注意要写在第一行,同时给style标签加入lang="scss"属性 */
@import "uview-plus/index.scss";
</style>
+168
View File
@@ -0,0 +1,168 @@
<script setup lang="ts">
import TnTabbarItem from "@tuniao/tnui-vue3-uniapp/components/tabbar/src/tabbar-item.vue";
import TnTabbar from "@tuniao/tnui-vue3-uniapp/components/tabbar/src/tabbar.vue";
import {storeToRefs} from "pinia";
import {useAppStore} from "@/stores/appStore.ts";
import { onMounted } from 'vue';
onMounted(() => {
// 获取当前选中的 Tab 索引
const currentTab = uni.getStorageSync('currentTab');
if (currentTab !== undefined) {
console.log('-++++++++++++++++++++++++++++++++++++++-',currentTab.value)
currentTab2.value = currentTab;
}
});
const currentTab2 = 0
const {tabbarIndex}=storeToRefs(useAppStore())
//处理导航栏点击事件
const changeHomePage = (index: number) => {
tabbarIndex.value = index.index;
console.log('index', tabbarIndex.value);
let url = '';
if (tabbarIndex.value === 0) {
url = `/pages/index/index?currentTab=${tabbarIndex.value}`;
} else if (tabbarIndex.value === 1) {
url = `/pages/creative/creative?currentTab=${tabbarIndex.value}`;
} else if (tabbarIndex.value === 2) {
url = `/pages/setting/setting?currentTab=${tabbarIndex.value}`;
}
console.log(url)
uni.redirectTo({
url
});
};
const handleTabbarClick = (index: number) => {
console.log('index',index)
tabbarIndex.value=index
if(tabbarIndex.value===0){
uni.navigateTo({
url:'/pages/index/index'
})
// uni.redirectTo ({
// url:'/pages/index/index'
// })
}
else if(tabbarIndex.value===1){
uni.navigateTo({
url:'/pages/creative/creative'
})
// uni.redirectTo ({
// url:'/pages/creative/creative'
// })
}else if(tabbarIndex.value===2){
uni.navigateTo({
url:'/pages/setting/setting'
})
// uni.redirectTo ({
// url:'/pages/setting/setting'
// })
}
}
// 导航栏数据
const tabbarData = [
{
name: '首页',
icon: 'home',
activeIcon: 'write-fill',
to: '/pages/index/index',
onClick: handleTabbarClick
},
{
name: '创意',
icon: 'edit-pen',
activeIcon: 'shop-fill',
to:'/pages/creative/creative',
onClick:handleTabbarClick
},
{
name: '我的',
icon: 'account',
activeIcon: 'my-circle-fill',
onClick:handleTabbarClick,
}
]
const handleChange = (index: number) => {
console.log(index);
}
const handleClick = (index: number) => {
console.log('click');
}
</script>
<template>
<!-- 底部导航-->
<!-- <TnTabbar v-model="currentTabbar" fixed @change="handleChange" @click="handleClick">-->
<!-- <TnTabbarItem-->
<!-- @click="handleClick"-->
<!-- v-for="(item, index) in tabbarData"-->
<!-- :key="index"-->
<!-- :icon="item.icon"-->
<!-- :active-icon="item.activeIcon"-->
<!-- :text="item.name"-->
<!-- />-->
<!-- </TnTabbar>-->
<view style="margin-top: 10%; margin-bottom: 10%;">
<fui-nav-bar custom background>
<view class="fui-search__box ">
<fui-tabs class="tabs_class" direction='column' color='#ACB0D0' :isSlider='false'
selectedColor='#17135F' :tabs="tabbarData" scale='1.5' @change="changeHomePage"
:center="false" :short="true" :scroll='false' itemPadding="25" :height='400' :current="currentTab2"
size='28' fontWeight='900' background></fui-tabs>
</view>
</fui-nav-bar>
</view>
<!-- <up-tabbar
:value="tabbarIndex"
:fixed="true"
:placeholder="false"
:safeAreaInsetBottom="false"
>
<up-tabbar-item :text="item.name" :icon="item.icon" @click="item.onClick" ></up-tabbar-item>
</template>
</up-tabbar> -->
</template>
<style scoped lang="scss">
.fui-search__box {
background: transparent;
width: 520rpx;
height: 48px;
margin-left: -0%;
box-sizing: border-box;
border-radius: 0px;
display: flex;
align-items: center;
justify-content: left;}
.tabs_class {
margin-top: -40%;
}
</style>
+168
View File
@@ -0,0 +1,168 @@
<script setup lang="ts">
import TnTabbarItem from "@tuniao/tnui-vue3-uniapp/components/tabbar/src/tabbar-item.vue";
import TnTabbar from "@tuniao/tnui-vue3-uniapp/components/tabbar/src/tabbar.vue";
import {storeToRefs} from "pinia";
import {useAppStore} from "@/stores/appStore.ts";
import { onMounted } from 'vue';
onMounted(() => {
// 获取当前选中的 Tab 索引
const currentTab = uni.getStorageSync('currentTab');
if (currentTab !== undefined) {
console.log('-++++++++++++++++++++++++++++++++++++++-',currentTab.value)
currentTab2.value = currentTab;
}
});
const currentTab2 = 0
const {tabbarIndex}=storeToRefs(useAppStore())
//处理导航栏点击事件
const changeHomePage = (index: number) => {
tabbarIndex.value = index.index;
console.log('index', tabbarIndex.value);
let url = '';
if (tabbarIndex.value === 0) {
url = `/pages/index/index?currentTab=${tabbarIndex.value}`;
} else if (tabbarIndex.value === 1) {
url = `/pages/creative/creative?currentTab=${tabbarIndex.value}`;
} else if (tabbarIndex.value === 2) {
url = `/pages/setting/setting?currentTab=${tabbarIndex.value}`;
}
console.log(url)
uni.redirectTo({
url
});
};
const handleTabbarClick = (index: number) => {
console.log('index',index)
tabbarIndex.value=index
if(tabbarIndex.value===0){
uni.navigateTo({
url:'/pages/index/index'
})
// uni.redirectTo ({
// url:'/pages/index/index'
// })
}
else if(tabbarIndex.value===1){
uni.navigateTo({
url:'/pages/creative/creative'
})
// uni.redirectTo ({
// url:'/pages/creative/creative'
// })
}else if(tabbarIndex.value===2){
uni.navigateTo({
url:'/pages/setting/setting'
})
// uni.redirectTo ({
// url:'/pages/setting/setting'
// })
}
}
// 导航栏数据
const tabbarData = [
{
name: '首页',
icon: 'home',
activeIcon: 'write-fill',
to: '/pages/index/index',
onClick: handleTabbarClick
},
{
name: '创意',
icon: 'edit-pen',
activeIcon: 'shop-fill',
to:'/pages/creative/creative',
onClick:handleTabbarClick
},
{
name: '我的',
icon: 'account',
activeIcon: 'my-circle-fill',
onClick:handleTabbarClick,
}
]
const handleChange = (index: number) => {
console.log(index);
}
const handleClick = (index: number) => {
console.log('click');
}
</script>
<template>
<!-- 底部导航-->
<!-- <TnTabbar v-model="currentTabbar" fixed @change="handleChange" @click="handleClick">-->
<!-- <TnTabbarItem-->
<!-- @click="handleClick"-->
<!-- v-for="(item, index) in tabbarData"-->
<!-- :key="index"-->
<!-- :icon="item.icon"-->
<!-- :active-icon="item.activeIcon"-->
<!-- :text="item.name"-->
<!-- />-->
<!-- </TnTabbar>-->
<view style="margin-top: 10%; margin-bottom: 10%;">
<fui-nav-bar custom background>
<view class="fui-search__box ">
<fui-tabs class="tabs_class" direction='column' color='#ACB0D0' :isSlider='false'
selectedColor='#17135F' :tabs="tabbarData" scale='1.5' @change="changeHomePage"
:center="false" :short="true" :scroll='false' itemPadding="25" :height='400' :current="currentTab2"
size='28' fontWeight='900' background></fui-tabs>
</view>
</fui-nav-bar>
</view>
<!-- <up-tabbar
:value="tabbarIndex"
:fixed="true"
:placeholder="false"
:safeAreaInsetBottom="false"
>
<up-tabbar-item :text="item.name" :icon="item.icon" @click="item.onClick" ></up-tabbar-item>
</template>
</up-tabbar> -->
</template>
<style scoped lang="scss">
.fui-search__box {
background: transparent;
width: 520rpx;
height: 48px;
margin-left: -0%;
box-sizing: border-box;
border-radius: 0px;
display: flex;
align-items: center;
justify-content: left;}
.tabs_class {
margin-top: -40%;
}
</style>
+45
View File
@@ -0,0 +1,45 @@
<script setup lang="ts">
import TnUpdateUserInfoPopup from 'tnuiv3p-tn-update-user-info-popup/index.vue'
import TnButton from '@tuniao/tnui-vue3-uniapp/components/button/src/button.vue'
import TnIcon from '@tuniao/tnui-vue3-uniapp/components/icon/src/icon.vue'
import { ref } from 'vue'
import {getBaseURL, getToken, uploadFile} from "@/utils/request.ts";
import {saveLoginInfo, updateUserInfo} from "@/composables/useCommon.ts";
const showPopup = ref<boolean>(false)
const nickname = ref<string>('')
const avatar = ref<string>('')
// 头像选择事件
const avatarChooseHandle = async (url: string) => {
// 换成自己的上传接口
const result=await uploadFile(url)
//更新用户头像和昵称
if(result){
avatar.value=result
}
}
const handleUpdateUser = async () => {
// 更新用户昵称
const user = await updateUserInfo({nickname: nickname.value,avatar_url:avatar.value})
// 保存用户
saveLoginInfo(user)
}
</script>
<template>
<TnButton size="sm" plain @click="() => (showPopup = true)">
<TnIcon name="edit"/>
修改头像和昵称 </TnButton>
<TnUpdateUserInfoPopup
v-model:show="showPopup"
v-model:nickname="nickname"
v-model:avatar="avatar"
@choose-avatar="avatarChooseHandle"
@confirm="handleUpdateUser"
/>
</template>
+16
View File
@@ -0,0 +1,16 @@
<!-- <template>
<view>
<fui-poster ref="generator" :width="560" :height="980" @ready="ready"></fui-poster>
</view>
</template>
<script>
function ready() {
this.isReady = true;
}
</script>
<style>
</style> -->
+666
View File
@@ -0,0 +1,666 @@
<script setup lang="ts">
import { computed, ref, watch } from 'vue';
import MyPopup from "@/components/common/MyPopup.vue";
import TnIcon from '@tuniao/tnui-vue3-uniapp/components/icon/src/icon.vue'
import { storeToRefs } from "pinia";
import { useAppStore } from "@/stores/appStore.ts";
import { onShow } from "@dcloudio/uni-app";
import $fui from '@/components/firstui/fui-clipboard';
const currentSwiperIndex = ref(0)
watch(currentSwiperIndex, () => {
console.log('currentSwiperIndex', currentSwiperIndex.value)
})
const loadingBackground = 'https://chinahu-ai-server.oss-cn-chengdu.aliyuncs.com/aidraw/image/temps/onloading_bg.jpg'
// uni.getStorageSync('name')
const { localTasks } = storeToRefs(useAppStore())
const AllList = computed(() => {
uni.setStorageSync('name', localTasks.value);
const returnvalue = uni.getStorageSync('name')
return returnvalue
})
// 如果任务完成则使用完成的图片,如果任务没有完成则是用loading图片
const swiperData = computed(() => {
if (localTasks.value.length === 0) {
return [loadingBackground]
}
return localTasks.value.map(item => {
return item.status === 1 ? item.output[0] : loadingBackground
})
})
// 当前任务的进度
const currentProgress = computed(() => {
if (localTasks.value.length === 0) {
return '暂无任务'
}
// 进度更新
const currentTask = localTasks.value[currentSwiperIndex.value]
if (currentTask && currentTask.status === 4) {
return currentTask.progress + '%'
} else if (currentTask && currentTask.status === 0 && currentTask.queue) {
return `对列:${currentTask.queue},预计:${currentTask.time_remained}s`
}
return ''
})
//当前任务的图片张数
const currentImageCount = computed(() => {
const currentTask = localTasks.value[currentSwiperIndex.value]
if (currentTask && currentTask.status === 1) {
return currentTask.output.length
} else {
return 0
}
})
const showProgress = computed(() => {
return localTasks.value[currentSwiperIndex.value]?.status !== 1
})
const progressAnimation = ref({}); // 存储动画数据
// 创建动画实例并设置动画效果
const createAnimation = () => {
const animation = uni.createAnimation({
duration: 500, // 动画时长
timingFunction: 'ease', // 动画缓动函数
});
// 设置从透明到不透明的动画效果
animation.opacity(0).step();
progressAnimation.value = animation.export();
return animation;
};
function handleChange(index : any) {
currentSwiperIndex.value = index.current; // 更新当前索引
}
const showPopup = defineModel({
default: false
})
// 处理触摸开始事件
const handleTouchStart = () => {
const animation = createAnimation();
// 在触摸开始时隐藏 progress-container
animation.opacity(0).step(); // 透明度设置为0
progressAnimation.value = animation.export(); // 应用动画
};
// 处理触摸结束事件
const handleTouchEnd = () => {
const animation = createAnimation();
// 在触摸结束时显示 progress-container
animation.opacity(1).step(); // 透明度设置为1
setTimeout(() => progressAnimation.value = animation.export(), 200)
};
const handleFindExecutingTaskIndex = () => {
return localTasks.value.findIndex(item => item.status === 4)
}
onShow(() => {
const excIndex = handleFindExecutingTaskIndex()
console.log('task onshow', excIndex)
if (excIndex !== -1) {
currentSwiperIndex.value = handleFindExecutingTaskIndex()
}
})
function checkContent(str) {
// 使用正则表达式判断是否是链接
const linkRegex = /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/;
if (linkRegex.test(str)) {
return 1; // 是链接
} else {
return 2; // 是文本
}
}
function judgeContent(input) {
// 定义图片链接的正则表达式
const imageRegex = /\.(jpg|jpeg|png|gif|bmp)$/i;
// 定义视频链接的正则表达式
const videoRegex = /\.(mp4|avi|mov|mkv|flv|wmv)$/i;
// 检查内容是否为空
if (!input) {
console.log('==============', '是空值')
return 0; // 内容为空
}
// 检查内容是否为图片链接
if (checkContent(input) == 1) {
if (imageRegex.test(input)) {
console.log('==============', '是图片')
return 1; // 是图片链接
}
// 检查内容是否为视频链接
else if (videoRegex.test(input)) {
console.log('==============', '是视频')
return 2; // 是视频链接
}
}
// 如果不是图片或视频链接,则认为是文本
if (checkContent(input) == 2) {
return 3; // 是文本
}
}
const StringImag = ref()
const StringCont = ref('')
const showOrSleep = ref(0)
const allValueList = ref([{
class: 'pic',
params: '', // 替换为实际参数
output: '', // 替换为实际输出
textImg: ' ' // 替换为实际图片路径
}])
// const allValueList = ref([]);
const generateParams = computed(() => {
console.log('----------------------------{{generateParams}}----------------', localTasks.value[currentSwiperIndex.value])
const output = localTasks.value[currentSwiperIndex.value]?.output[currentSwiperIndex.value];
const contentType = judgeContent(output);
allValueList.value = []
// 1是图片 2是视频 3是文本
if (contentType === 0) {
showOrSleep.value = 0;
} else if (contentType === 1) {
// 图片
showOrSleep.value = 0;
allValueList.value.push({
class: 'pic',
params: localTasks.value[currentSwiperIndex.value]?.params || '', // 替换为实际参数
output: output, // 替换为实际输出
textImg: ' ' // 替换为实际图片路径
});
console.log('----------------------------{{generateParams}}------allValueList----------', allValueList.value)
StringImag.value = output;
StringCont.value = '';
console.log('----------output---showOrSleep.value = 0;---------', output)
} else if (contentType === 2) {
allValueList.value.push({
class: 'video',
params: localTasks.value[currentSwiperIndex.value]?.params || '', // 替换为实际参数
output: output, // 替换为实际输出
textImg: ' ' // 替换为实际图片路径
});
showOrSleep.value = 1;
StringCont.value = "";
StringImag.value = "output";
console.log('----------output---showOrSleep.value = 1;---------', output)
} else if (contentType === 3) {
allValueList.value.push({
class: 'text',
params: localTasks.value[currentSwiperIndex.value]?.params || '', // 替换为实际参数
output: output, // 替换为实际输出
textImg: localTasks.value[currentSwiperIndex.value]?.params?.image_path_mask // 替换为实际图片路径
});
showOrSleep.value = 2;
StringCont.value = output;
console.log('----------output---showOrSleep.value = 2;---------', output, allValueList)
StringImag.value = localTasks.value[currentSwiperIndex.value]?.params?.image_path_mask;
}
// return localTasks.value[currentSwiperIndex.value]?.params;
})
/*保存到相册*/
const handleSave = () => {
const currentTask = localTasks.value[currentSwiperIndex.value]
if (currentTask && currentTask.status === 1) {
saveImage(currentTask.output[0])
}
}
const handleGotoHistory = () => {
console.log("我前往历史记录页面",)
uni.redirectTo({ url: '/pages/history/history_fui/history_fui' })
// uni.redirectTo({ url: '/pages/index/index' });
}
// function handleGotoHistory() {
// console.log("我前往历史记录页面")
// uni.redirectTo({ url: '/pages/history/history_fui/history_fui' })
// }
// 下载网络图片并保存到相册
const saveImage = (url : string) => {
// 第一步:下载图片
uni.downloadFile({
url: url, // 图片的网络地址
success: (res) => {
if (res.statusCode === 200) {
// 第二步:下载成功后,获取本地路径
const localPath = res.tempFilePath;
console.log('localPath', localPath);
// 第三步:保存图片到相册
uni.saveImageToPhotosAlbum({
filePath: localPath,
success: () => {
uni.showToast({
title: '图片已保存',
icon: 'success'
});
},
fail: (error) => {
uni.showToast({
title: '保存失败',
icon: 'none'
});
console.error('保存图片失败:', error);
}
});
} else {
uni.showToast({
title: '下载失败',
icon: 'none'
});
}
},
fail: (error) => {
uni.showToast({
title: '下载失败',
icon: 'none'
});
console.error('下载图片失败:', error);
}
});
}
/**预览图片 */
const handlePreview = () => {
const currentTask = localTasks.value[currentSwiperIndex.value]
if (currentTask && currentTask.status === 1) {
console.log("preview")
uni.previewImage({ urls: currentTask.output })
}
}
const fabs = [{
name: 'edit',
text: '复制文本'
},
// {
// name: 'share',
// text: '分享海报'
// },
]
const show = ref(false)
function handleClick(e, StringTxt) {
if (e.index == 0) {
console.log('---------------(e)------------', e)
uni.setClipboardData({
data: StringTxt, // 需要设置到剪切板的内容
showToast: true, // 是否显示提示,默认为true
success: function () {
console.log('复制成功');
},
fail: function (err) {
console.error('复制失败', err);
}
});
}
else if (e.index == 1) {
uni.value.toast("还在开发中.....");
}
}
const isLeft = true
const current = ref(0)
const SWitems = [{
background: '#09BE4F'
}, {
background: '#FFB703'
}, {
background: '#B2B2B2'
}]
function change(e) {
current.value = e.detail.current;
}
console.log("--------****************------------", AllList, localTasks)
</script>
<template>
<!-- <fui-dialog :show="show" content="还在开发中....." maskClosable ></fui-dialog> -->
<MyPopup v-model="showPopup">
<view>
<fui-background-image style='z-index: 1;'
src="https://chinahu-ai-server.oss-cn-chengdu.aliyuncs.com/aidraw/image/temps/67873d6c232a3c5d52240dd6/%C3%A7%C2%A4%C2%BE%C3%A4%C2%BA%C2%A4APP.jpg">
</fui-background-image>
<view style="padding: 200rpx 40rpx 0 40rpx;position: relative;" @touchend="handleTouchEnd"
@touchstart="handleTouchStart">
<!-- v-if="checkContent(currentSwiperIndex) == 1" -->
<view>
<view>
<!-- <view v-if="showOrSleep==0">
<up-swiper @click="handlePreview" @change="handleChange" :current="currentSwiperIndex"
:list="swiperData" previousMargin="20" nextMargin="20" imgMode="aspectFill" height="500"
indicator indicatorMode="line" circular :autoplay="false" radius="20" keyName="url"
indicatorStyle="bottom">
</up-swiper>
</view> -->
<!-- <view v-else-if="showOrSleep==1">
<view>
<view class="fui-list__item CardStyle">
<video
style="width: 80%; height: 480px; margin-top: 0%; background-color:transparent; "
id="myVideo" :src="StringImag" controls></video>
</view>
</view>
</view> -->
<!-- <view style="margin-top: 6%;">
<video style="width: 100%; height: 390px; background-color:transparent;" id="myVideo" :src="StringImag"
controls></video>
</view> -->
<!-- <view v-else-if="showOrSleep==2">
<fui-background-image
src="@/src/static/Home2 (1).jpgHome2(1).jpg">
</fui-background-image>
<view style="margin-left:10% ;margin-right: 10%;margin-top: 15%;">
<scroll-view scroll-y="true" class="scroll-Y">
<view style="text-align: center;">
<image style="width: 200px; height: 300px; background-color: #eeeeee;"
mode="scaleToFill" :src="StringImag"></image>
</view>
<fui-parse-group class="custom-view" :thBgcolor="false">
<fui-parse :nodes="StringCont" language="html"></fui-parse>
</fui-parse-group>
</scroll-view>
<fui-fab :position="isLeft?'left':'right'" :fabs="fabs"
@click="handleClick($event,StringCont)"></fui-fab>
</view>
</view> -->
<view>
<!-- --------------------------------------------------------------------------------------------- -->
<!-- currentImageCount -->
<!-- <fui-swiper-dot :items="currentImageCount " :current="current"> -->
<swiper class="fui-banner__wrap" @change="change" circular :indicator-dots="false"
:autoplay='true' :interval="10000" :duration="150">
<swiper-item v-for="(item,index) in AllList" :key="index">
<view>
<!-- <view v-if="judgeContent(item.output[0]) == 0 ">
<fui-empty src="/static/images/component/empty/img_news_3x.png" title="暂无新消息"></fui-empty>
</view> -->
<view v-if="judgeContent(item.output[0]) == 1 ">
<up-swiper @click="handlePreview" @change="handleChange"
:current="currentSwiperIndex" :list="swiperData" previousMargin="20"
nextMargin="20" imgMode="aspectFill" height="500" indicator
indicatorMode="line" circular :autoplay="false" radius="20"
keyName="url" indicatorStyle="bottom">
</up-swiper>
<view style="margin-top: 6rpx;">
<view>
<TnIcon name="starry" size="50" class="params-icon" />
<span class="tn-text ">提示词</span>
</view>
<span style="padding: 10rpx;"
class="tn-text tn-text-ellipsis-3 ">{{ item.params.positive }}</span>
<view>
</view>
</view>
</view>
<view v-if="judgeContent(item.output[0]) == 2">
<view>
<view class="fui-list__item CardStyle">
<video
style="width: 80%; height: 480px; margin-top: 0%; background-color:transparent; "
id="myVideo" :src="item.output[0]" controls></video>
</view>
</view>
</view>
<view v-if="judgeContent(item.output[0]) == 3">
<view style="margin-left:10% ;margin-right: 10%;margin-top: 0%;">
<scroll-view scroll-y="true" class="scroll-Y">
<view style="text-align: center;">
<image
style="width: 200px; height: 300px; background-color: #eeeeee;"
mode="scaleToFill"
:src="item?.params?.image_path_origin || loadingBackground">
</image>
</view>
<fui-parse-group class="custom-view" :thBgcolor="false">
<fui-parse :nodes="item.output[0]"
language="html"></fui-parse>
</fui-parse-group>
</scroll-view>
<fui-fab :position="isLeft?'left':'right'" :fabs="fabs"
@click="handleClick($event,item.output[0])"></fui-fab>
</view>
</view>
</view>
</swiper-item>
</swiper>
<!-- </fui-swiper-dot> -->
<!-- --------------------------------------------------------------------------------------------- -->
</view>
<view>
<view class="progress-container" v-if="showProgress" :animation="progressAnimation">
<view class="tn-text-center">
<TnIcon name="starry" size="100" color="tn-white" />
</view>
<view class="tn-text tn-text-center tn-text-bold tn-white_text">
{{ currentProgress }}
</view>
<view class="tn-text-sm tn-text-center tn-white_text">
关闭页面不影响生成结果
</view>
<view class="tn-text-sm tn-text-center tn-white_text">
视频生成时间较长稍后可到
</view>
<view class="tn-text-sm tn-text-center tn-white_text">
绘图历史中查询
</view>
</view>
</view>
<!--显示图片数量-->
<view class="image-count">
<TnIcon style="color: gainsboro;" name="image"></TnIcon>
<span style="color: gainsboro;margin-left: 3rpx;"
class="tn-text-sm">{{currentImageCount}}</span>
</view>
</view>
</view>
</view>
<!-- 底部按钮-->
<view>
</view>
<view class="bottom-container">
<view style="display: flex; ">
<!-- <view class="bottom-icon">
<tn-icon name="download-simple" size="50rpx" @click="handleSave"></tn-icon>
</view> -->
<!-- <view class="bottom-icon">
<tn-icon name="send" size="50rpx"></tn-icon>
</view> -->
</view>
<!-- <view >
<button style="background-color: transparent; margin: 0; padding: 0; text-align: left;" open-type="contact">
<tn-icon name="chat" size="50rpx"></tn-icon>
</button>
</view> -->
</view>
</view>
<!-- <view v-else-if="showOrSleep==3">
<fui-background-image
src="@/src/static/Home2 (1).jpgHome2(1).jpg">
</fui-background-image>
<view style="margin-left:10% ;margin-right: 10%;margin-top: 15%;">
<scroll-view scroll-y="true" class="scroll-Y">
<view style="text-align: center;">
<image style="width: 200px; height: 300px; background-color: #eeeeee;" mode="scaleToFill"
:src="StringImag"></image>
</view>
</scroll-view>
<fui-fab :position="isLeft?'left':'right'" :fabs="fabs"
@click="handleClick($event,StringCont)"></fui-fab>
</view>
</view> -->
<view>
</view>
</MyPopup>
</template>
<style scoped lang="scss">
.fui-banner__item {
width: 100%;
height: 1400rpx;
color: #FFFFFF;
display: flex;
align-items: center;
justify-content: center;
font-size: 34rpx;
font-weight: 600;
}
.fui-banner__wrap {
z-index: 110;
height: 1500rpx;
}
// -------------------------------
.CardStyle {
margin-top: 20%;
text-align: center;
}
.fui-section__title {
margin-left: 32rpx;
}
.custom-view {
padding: 10px;
/* 内边距 */
color: #333;
/* 文本颜色 */
font-size: 16px;
/* 字体大小 */
}
.scroll-Y {
height: 1200rpx;
}
.fui-scroll__view {
width: 100%;
height: 600rpx;
}
.progress-container {
z-index: 110;
position: absolute;
top: 35%;
left: 50%;
transform: translate(-50%, -50%);
}
.bottom-container {
display: flex;
justify-content: space-between;
align-items: center;
position: fixed;
bottom: 40rpx;
width: 100%;
padding-bottom: 5%;
padding-left: 5%;
padding-right: 5%;
}
.bottom-icon {
position: relative;
display: flex;
justify-content: center;
align-items: center;
width: 90rpx;
height: 90rpx;
color: $u-primary-lighten;
margin-right: 20rpx;
background-color: $u-bg-color;
border-radius: 50%;
font-size: 70rpx;
box-shadow: 0 5px 15px rgba(46, 54, 80, .3);
overflow: hidden;
/* 确保水波纹不超出圆形按钮 */
}
.bottom-icon::before {
content: '';
position: absolute;
width: 100%;
height: 100%;
border-radius: 50%;
background: rgba(0, 0, 0, 0.2);
transform: scale(0);
opacity: 0;
pointer-events: none;
transition: transform 0.6s, opacity 0.6s;
}
.bottom-icon:active::before {
transform: scale(4);
opacity: 1;
}
.params-icon {
color: gray;
margin-right: 20rpx;
}
.image-count {
position: absolute;
right: 120rpx;
bottom: 120rpx;
}
</style>
+502
View File
@@ -0,0 +1,502 @@
<script setup lang="ts">
import { computed, ref, watch } from 'vue';
import MyPopup from "@/components/common/MyPopup.vue";
import TnIcon from '@tuniao/tnui-vue3-uniapp/components/icon/src/icon.vue'
import { storeToRefs } from "pinia";
import { useAppStore } from "@/stores/appStore.ts";
import { onShow } from "@dcloudio/uni-app";
import $fui from '@/components/firstui/fui-clipboard';
const currentSwiperIndex = ref(0)
watch(currentSwiperIndex, () => {
console.log('currentSwiperIndex', currentSwiperIndex.value)
})
const loadingBackground = 'https://static.51easyai.com/comfy/onloading_bg.jpg'
const { localTasks } = storeToRefs(useAppStore())
// 如果任务完成则使用完成的图片,如果任务没有完成则是用loading图片
const swiperData = computed(() => {
if (localTasks.value.length === 0) {
return [loadingBackground]
}
return localTasks.value.map(item => {
return item.status === 1 ? item.output[0] : loadingBackground
})
})
// 当前任务的进度
const currentProgress = computed(() => {
if (localTasks.value.length === 0) {
return '暂无任务'
}
// 进度更新
const currentTask = localTasks.value[currentSwiperIndex.value]
if (currentTask && currentTask.status === 4) {
return currentTask.progress + '%'
} else if (currentTask && currentTask.status === 0 && currentTask.queue) {
return `对列:${currentTask.queue},预计:${currentTask.time_remained}s`
}
return ''
})
//当前任务的图片张数
const currentImageCount = computed(() => {
const currentTask = localTasks.value[currentSwiperIndex.value]
if (currentTask && currentTask.status === 1) {
return currentTask.output.length
} else {
return 0
}
})
const showProgress = computed(() => {
return localTasks.value[currentSwiperIndex.value]?.status !== 1
})
const progressAnimation = ref({}); // 存储动画数据
// 创建动画实例并设置动画效果
const createAnimation = () => {
const animation = uni.createAnimation({
duration: 500, // 动画时长
timingFunction: 'ease', // 动画缓动函数
});
// 设置从透明到不透明的动画效果
animation.opacity(0).step();
progressAnimation.value = animation.export();
return animation;
};
function handleChange(index : any) {
currentSwiperIndex.value = index.current; // 更新当前索引
}
const showPopup = defineModel({
default: false
})
// 处理触摸开始事件
const handleTouchStart = () => {
const animation = createAnimation();
// 在触摸开始时隐藏 progress-container
animation.opacity(0).step(); // 透明度设置为0
progressAnimation.value = animation.export(); // 应用动画
};
// 处理触摸结束事件
const handleTouchEnd = () => {
const animation = createAnimation();
// 在触摸结束时显示 progress-container
animation.opacity(1).step(); // 透明度设置为1
setTimeout(() => progressAnimation.value = animation.export(), 200)
};
const handleFindExecutingTaskIndex = () => {
return localTasks.value.findIndex(item => item.status === 4)
}
onShow(() => {
const excIndex = handleFindExecutingTaskIndex()
console.log('task onshow', excIndex)
if (excIndex !== -1) {
currentSwiperIndex.value = handleFindExecutingTaskIndex()
}
})
function checkContent(str) {
// 使用正则表达式判断是否是链接
const linkRegex = /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/;
if (linkRegex.test(str)) {
return 1; // 是链接
} else {
return 2; // 是文本
}
}
function judgeContent(input) {
// 定义图片链接的正则表达式
const imageRegex = /\.(jpg|jpeg|png|gif|bmp)$/i;
// 定义视频链接的正则表达式
const videoRegex = /\.(mp4|avi|mov|mkv|flv|wmv)$/i;
// 检查内容是否为空
if (!input) {
console.log('==============', '是空值')
return 0; // 内容为空
}
// 检查内容是否为图片链接
if (checkContent(input) == 1) {
if (imageRegex.test(input)) {
console.log('==============', '是图片')
return 1; // 是图片链接
}
// 检查内容是否为视频链接
else if (videoRegex.test(input)) {
console.log('==============', '是视频')
return 2; // 是视频链接
}
}
// 如果不是图片或视频链接,则认为是文本
if (checkContent(input) == 2) {
return 3; // 是文本
}
}
const StringImag = ref()
const StringCont = ref('')
const showOrSleep = ref(0)
const generateParams = computed(() => {
console.log('----------------------------{{generateParams}}----------------', localTasks.value[currentSwiperIndex.value])
const output = localTasks.value[currentSwiperIndex.value]?.output[currentSwiperIndex.value];
const contentType = judgeContent(output);
// 1是图片 2是视频 3是文本
if (contentType === 0) {
showOrSleep.value = 1;
} else if (contentType === 1) {
showOrSleep.value = 0;
StringImag.value = output;
StringCont.value = '';
console.log('----------output---showOrSleep.value = 0;---------', output)
} else if (contentType === 2) {
showOrSleep.value = 1;
StringCont.value = "";
StringImag.value = "output";
console.log('----------output---showOrSleep.value = 1;---------', output)
} else if (contentType === 3) {
showOrSleep.value = 2;
StringCont.value = output;
console.log('----------output---showOrSleep.value = 2;---------', output)
StringImag.value = localTasks.value[currentSwiperIndex.value]?.params?.image_path_mask;
}
// return localTasks.value[currentSwiperIndex.value]?.params;
})
/*保存到相册*/
const handleSave = () => {
const currentTask = localTasks.value[currentSwiperIndex.value]
if (currentTask && currentTask.status === 1) {
saveImage(currentTask.output[0])
}
}
const handleGotoHistory = () => {
uni.navigateTo({ url: '/pages/history/history_fui/history_fui' })
}
// 下载网络图片并保存到相册
const saveImage = (url : string) => {
// 第一步:下载图片
uni.downloadFile({
url: url, // 图片的网络地址
success: (res) => {
if (res.statusCode === 200) {
// 第二步:下载成功后,获取本地路径
const localPath = res.tempFilePath;
console.log('localPath', localPath);
// 第三步:保存图片到相册
uni.saveImageToPhotosAlbum({
filePath: localPath,
success: () => {
uni.showToast({
title: '图片已保存',
icon: 'success'
});
},
fail: (error) => {
uni.showToast({
title: '保存失败',
icon: 'none'
});
console.error('保存图片失败:', error);
}
});
} else {
uni.showToast({
title: '下载失败',
icon: 'none'
});
}
},
fail: (error) => {
uni.showToast({
title: '下载失败',
icon: 'none'
});
console.error('下载图片失败:', error);
}
});
}
/**预览图片 */
const handlePreview = () => {
const currentTask = localTasks.value[currentSwiperIndex.value]
if (currentTask && currentTask.status === 1) {
console.log("preview")
uni.previewImage({ urls: currentTask.output })
}
}
const fabs = [{
name: 'edit',
text: '复制文本'
},
// {
// name: 'share',
// text: '分享海报'
// },
]
const show = ref(false)
function handleClick(e, StringTxt) {
if (e.index == 0) {
console.log('---------------(e)------------', e)
uni.setClipboardData({
data: StringTxt, // 需要设置到剪切板的内容
showToast: true, // 是否显示提示,默认为true
success: function () {
console.log('复制成功');
},
fail: function (err) {
console.error('复制失败', err);
}
});
}
else if (e.index == 1) {
uni.value.toast("还在开发中.....");
}
}
const isLeft = true
</script>
<template>
<!-- <fui-dialog :show="show" content="还在开发中....." maskClosable ></fui-dialog> -->
<MyPopup v-model="showPopup">
<view >
<view style="padding: 200rpx 40rpx 0 40rpx;position: relative;" @touchend="handleTouchEnd"
@touchstart="handleTouchStart">
<!-- v-if="checkContent(currentSwiperIndex) == 1" -->
<view>
<view>
<view v-if="showOrSleep==0">
<up-swiper @click="handlePreview" @change="handleChange" :current="currentSwiperIndex"
:list="swiperData" previousMargin="20" nextMargin="20" imgMode="aspectFill" height="500"
indicator indicatorMode="line" circular :autoplay="false" radius="20" keyName="url"
indicatorStyle="bottom">
</up-swiper>
</view>
<view v-else-if="showOrSleep==1">
<view >
<view class="fui-list__item CardStyle">
<!-- <image class="fui-cover" :src="`${resUrl}/cooperate/dark/img_banner_3x.png`" mode="widthFix"></image> -->
<video style="width: 80%; height: 480px; margin-top: 0%; background-color:transparent; " id="myVideo"
:src="StringImag" controls></video>
</view>
</view>
</view>
<!-- <view style="margin-top: 6%;">
<video style="width: 100%; height: 390px; background-color:transparent;" id="myVideo" :src="StringImag"
controls></video>
</view> -->
<view v-else-if="showOrSleep==2">
<fui-background-image
src="@/src/static/Home2 (1).jpgHome2(1).jpg">
</fui-background-image>
<view style="margin-left:10% ;margin-right: 10%;margin-top: 15%;">
<scroll-view scroll-y="true" class="scroll-Y">
<view style="text-align: center;">
<image style="width: 200px; height: 300px; background-color: #eeeeee;" mode="scaleToFill"
:src="StringImag"></image>
</view>
<fui-parse-group class="custom-view" :thBgcolor="false">
<fui-parse :nodes="StringCont" language="html"></fui-parse>
</fui-parse-group>
</scroll-view>
<fui-fab :position="isLeft?'left':'right'" :fabs="fabs"
@click="handleClick($event,StringCont)"></fui-fab>
</view>
</view>
<!--显示图片数量-->
<view class="image-count">
<TnIcon style="color: gainsboro;" name="image"></TnIcon>
<span style="color: gainsboro;margin-left: 3rpx;"
class="tn-text-sm">{{currentImageCount}}</span>
</view>
</view>
<view class="progress-container" v-if="showProgress" :animation="progressAnimation">
<view class="tn-text-center">
<TnIcon name="starry" size="100" color="tn-white" />
</view>
<view class="tn-text tn-text-center tn-text-bold tn-white_text">
{{ currentProgress }}
</view>
<view class="tn-text-sm tn-text-center tn-white_text">
关闭页面不影响生成结果
</view>
</view>
</view>
<view style="margin-top: 6rpx;">
<view>
<TnIcon name="starry" size="50" class="params-icon" />
<span class="tn-text ">提示词</span>
</view>
<span style="padding: 10rpx;"
class="tn-text tn-text-ellipsis-3">{{ generateParams?.positive }}</span>
<view>
</view>
</view>
</view>
<!-- 底部按钮-->
<view class="bottom-container">
<view style="display: flex; ">
<view class="bottom-icon">
<tn-icon name="download-simple" size="50rpx" @click="handleSave"></tn-icon>
</view>
<view class="bottom-icon">
<tn-icon name="send" size="50rpx"></tn-icon>
</view>
</view>
<view class="bottom-icon">
<tn-icon name="right-arrow" size="50rpx" @click="handleGotoHistory"></tn-icon>
</view>
</view>
</view>
<!-- <view v-else-if="showOrSleep==3">
<fui-background-image
src="@/src/static/Home2 (1).jpgHome2(1).jpg">
</fui-background-image>
<view style="margin-left:10% ;margin-right: 10%;margin-top: 15%;">
<scroll-view scroll-y="true" class="scroll-Y">
<view style="text-align: center;">
<image style="width: 200px; height: 300px; background-color: #eeeeee;" mode="scaleToFill"
:src="StringImag"></image>
</view>
</scroll-view>
<fui-fab :position="isLeft?'left':'right'" :fabs="fabs"
@click="handleClick($event,StringCont)"></fui-fab>
</view>
</view> -->
<view>
</view>
</MyPopup>
</template>
<style scoped lang="scss">
.CardStyle{
// margin-top: 40%;
text-align: center;
}
.fui-section__title {
margin-left: 32rpx;
}
.custom-view {
padding: 10px;
/* 内边距 */
color: #333;
/* 文本颜色 */
font-size: 16px;
/* 字体大小 */
}
.scroll-Y {
height: 1200rpx;
}
.fui-scroll__view {
width: 100%;
height: 600rpx;
}
.progress-container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.bottom-container {
display: flex;
justify-content: space-between;
align-items: center;
position: fixed;
bottom: 40rpx;
width: 100%;
padding-bottom: 5%;
padding-left: 5%;
padding-right: 5%;
}
.bottom-icon {
position: relative;
display: flex;
justify-content: center;
align-items: center;
width: 90rpx;
height: 90rpx;
color: $u-primary-lighten;
margin-right: 20rpx;
background-color: $u-bg-color;
border-radius: 50%;
font-size: 70rpx;
box-shadow: 0 5px 15px rgba(46, 54, 80, .3);
overflow: hidden;
/* 确保水波纹不超出圆形按钮 */
}
.bottom-icon::before {
content: '';
position: absolute;
width: 100%;
height: 100%;
border-radius: 50%;
background: rgba(0, 0, 0, 0.2);
transform: scale(0);
opacity: 0;
pointer-events: none;
transition: transform 0.6s, opacity 0.6s;
}
.bottom-icon:active::before {
transform: scale(4);
opacity: 1;
}
.params-icon {
color: gray;
margin-right: 20rpx;
}
.image-count {
position: absolute;
right: 120rpx;
bottom: 120rpx;
}
</style>
+189
View File
@@ -0,0 +1,189 @@
<template>
<view class="page-container">
<!-- 页面上的其他内容 -->
<view class="content">
<slot></slot>
</view>
<!-- 可拖动按钮的区域 -->
<movable-area class="movable-area" :scale="true" :out-of-bounds="false">
<!-- 可拖动按钮 -->
<movable-view
class="draggable-btn"
:style="buttonStyle"
:direction="'all'"
:scale="true"
:inertia="true"
:momentum="true"
@change="onMoveChange"
>
<!-- 显示拖动按钮 -->
<view class="button">
拖动我
<view class="wave" :style="waveStyle"></view>
</view>
</movable-view>
</movable-area>
</view>
</template>
<script setup>
import { ref, computed } from 'vue';
const progress = ref(0);
// 计算按钮的样式
const buttonStyle = computed(() => {
return {
position: 'absolute',
zIndex: 9999,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
width: '100px',
height: '100px',
backgroundColor: 'transparent', // 背景透明
color: '#ff6a00',
borderRadius: '50%',
fontSize: '16px',
cursor: 'pointer',
pointerEvents: 'auto', // 确保按钮响应触摸
};
});
// 计算波浪样式
const waveStyle = computed(() => {
const waveHeight = (100 - progress.value) * 0.4;
const wavePosition = (progress.value) * 0.3;
return {
position: 'absolute',
bottom: `${wavePosition}px`,
left: 0,
right: 0,
height: `${waveHeight}px`,
width: '100%',
borderRadius: '50%',
backgroundColor: 'rgba(255, 255, 255, 0.6)',
transform: `rotate(${progress.value * 1.8}deg)`,
};
});
const updateProgress = (percent) => {
progress.value = percent;
};
const onMoveChange = (e) => {
console.log('移动后的位置:', e.detail);
};
setInterval(() => {
if (progress.value < 100) {
progress.value += 1;
}
}, 100);
</script>
<style scoped>
/* 页面容器样式 */
.page-container {
position: relative;
width: 100%;
height: 100vh;
background-color: #f1f1f1;
overflow: hidden; /* 防止拖动区域超出屏幕时出现滚动条 */
}
/* 内容区域 */
.content {
position: relative;
z-index: 1;
}
/* 可拖动区域样式 */
.movable-area {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 999; /* 保证拖动按钮的区域在内容上面 */
pointer-events: none; /* 禁用可拖动区域的交互,只让按钮响应触摸 */
}
/* 可拖动按钮的样式 */
.draggable-btn {
position: absolute;
z-index: 9999;
display: flex;
justify-content: center;
align-items: center;
width: 100px;
height: 100px;
background-color: transparent; /* 背景透明 */
color: #ff6a00;
border-radius: 50%;
font-size: 16px;
cursor: pointer;
pointer-events: auto; /* 使按钮响应触摸 */
}
/* 按钮内容样式 */
.btn-content {
display: flex;
justify-content: center;
align-items: center;
position: relative;
}
.button{
display: flex;
justify-content: center;
align-items: center;
width: 160rpx;
height: 160rpx;
background-color: #ff6a00;
border-radius: 50%;
color: #fff;
font-size: 16px;
animation: verticalMove 2s ease-in-out infinite;
}
/* 波浪样式 */
.wave {
position: absolute;
width: 100%;
height: 10px;
background-color: rgba(255, 255, 255, 0.6);
border-radius: 50%;
bottom: 0;
left: 0;
animation: waveAnimation 2s ease-in-out infinite;
}
/* 上下移动动画 */
@keyframes verticalMove {
0% {
transform: translateY(0);
}
50% {
transform: translateY(-20px); /* 上移 */
}
100% {
transform: translateY(0); /* 恢复到原位 */
}
}
@keyframes waveAnimation {
0% {
transform: translateX(0);
}
50% {
transform: translateX(30px);
}
100% {
transform: translateX(0);
}
}
</style>
+155
View File
@@ -0,0 +1,155 @@
<template>
<view>
<movable-area class="movable-area" :scale-area="false">
<movable-view
id="movable-view"
class="movable-view" :class="!isRemove?'animation-info':''" style="pointer-events: auto;"
@click="clickBtn" @touchstart="touchstart" @touchend="touchend" @change="onChange" direction="all"
inertia="true" :x="btnPositon.x" :y="btnPositon.y" :disabled="disabled" :out-of-bounds="true" :damping="200" :friction="100">
<slot>
<view class="content-default">
<slot name="text-content"></slot>
</view>
</slot>
</movable-view>
</movable-area>
</view>
</template>
<script setup lang="ts">
import { reactive, ref, withDefaults, nextTick, watch} from 'vue';
import {onLoad} from "@dcloudio/uni-app";
interface Props{
disabled?: boolean,
canDocking?: boolean,
bottomPx?: number,
rightPx?: number
}
const props = withDefaults(defineProps<Props>(), {
disabled: false,
canDocking: true,
bottomPx: 30,
rightPx: 0
})
const windowWidth=ref(0)
const windowHeight=ref(0)
const btnWidth=ref(0)
const btnHeight=ref(0)
/** 按钮位置 */
const btnPositon =defineModel({
default:{
x:10000,
y:10000
}
})
const old = reactive({
x: 0,
y: 0
})
// watch(old,()=>{
// console.log('old position',old)
// console.log('btn position',btnPositon.value)
// },{
// deep:true
// })
const getSysInfo=()=> {
let sysInfo = uni.getSystemInfoSync()
windowWidth.value = sysInfo.windowWidth
windowHeight.value = sysInfo.windowHeight
btnPositon.value.x = sysInfo.windowWidth - props.rightPx
btnPositon.value.y = sysInfo.windowHeight - props.bottomPx -300
}
onLoad(()=>{
nextTick(()=>{
getSysInfo()
})
})
//移动按钮
const onChange=(e)=> {
old.x = e.detail.x
old.y = e.detail.y
}
const isRemove = ref(true)
//开始移动
const touchstart=(e)=> {
isRemove.value = true
}
//结束移动
const touchend=(e)=> {
if (props.canDocking && old.x !== undefined) {
btnPositon.value.x = old.x
btnPositon.value.y = old.y
let bWidth = (windowWidth.value - btnWidth.value) / 2
console.log('bwidth',bWidth)
if (btnPositon.value.x <= 0 || (btnPositon.value.x >= 0 && btnPositon.value.x <= bWidth)) {
nextTick(res => {
btnPositon.value.x = 0
})
} else {
nextTick(res => {
// 靠右边缘
btnPositon.value.x = windowWidth.value -
btnWidth.value
})
}
isRemove.value = false
}
}
// 自定义事件
const emit = defineEmits(['clickBtn'])
//点击按钮
// 点击按钮
const clickBtn = () => {
emit('clickBtn',null);
};
</script>
<style scoped lang="scss">
.movable-view {
width: 100rpx;
height: 100rpx;
background: transparent;
font-size: 26rpx;
touch-action: none;
display: flex;
align-items: center;
justify-content: center;
}
.content-default{
width: 100rpx;
height: 100rpx;
background: linear-gradient(360deg, $u-primary-dark 0%, $u-primary 100%);
box-shadow: 0 4rpx 12rpx 0 #ADC3F8;
border-radius: 50rpx;
color: #FFFFFF;
display: flex;
align-items: center;
justify-content: center;
animation: verticalMove 2s ease-in-out infinite;
}
.animation-info {
transition: left .25s ease;
}
.movable-area {
width: 100%;
height: 100%;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 999999 !important;
pointer-events: none;
}
</style>
+19
View File
@@ -0,0 +1,19 @@
<script setup lang="ts">
import { ref } from 'vue';
import { onPageScroll } from '@dcloudio/uni-app';
// 创建响应式数据 scrollTop
const scrollTop = ref(0);
// onPageScroll 方法来更新 scrollTop 的值
onPageScroll((e) => {
scrollTop.value = e.scrollTop;
});
</script>
<template>
<up-back-top :scroll-top="scrollTop"></up-back-top>
</template>
<style scoped lang="scss">
</style>
+24
View File
@@ -0,0 +1,24 @@
<script setup lang="ts">
import CustomSlider from "@/components/dynamic/CustomSlider.vue";
const title = '标题'
const subTitle = '子标题'
const thumb = 'https://img12.360buyimg.com/imagetools/jfs/t1/149875/20/11964/135544/5f7c42a5E6c6ea73d/b93c6e553725ddfb.png'
</script>
<template>
<up-card :title="title" :sub-title="subTitle" margin="15rpx">
<template #body>
<CustomSlider/>
</template>
<template #foot>
<up-button type="primary">操作</up-button>
</template>
</up-card>
</template>
<style scoped lang="scss">
</style>
+23
View File
@@ -0,0 +1,23 @@
<script setup lang="ts">
import TnNavbar from "@tuniao/tnui-vue3-uniapp/components/navbar/src/navbar.vue";
</script>
<template>
<!-- <TnNavbar-->
<!-- :opacity="0.5"-->
<!-- height="45px"-->
<!-- frosted-->
<!-- :bottom-shadow="false"-->
<!-- :safe-area-inset-right="true"></TnNavbar>-->
<up-navbar-mini
:autoBack="true"
homeUrl="/pages/index/index"
>
</up-navbar-mini>
</template>
<style scoped lang="scss">
</style>
+26
View File
@@ -0,0 +1,26 @@
<script setup lang="ts">
import TnPopup from '@tuniao/tnui-vue3-uniapp/components/popup/src/popup.vue'
const showPopup = defineModel({default:false})
</script>
<template>
<!-- <fui-bottom-popup :show="showPopup" > -->
<TnPopup
top="100rpx"
style="z-index: 100"
v-model="showPopup"
close-btn
width="100%" height="100%">
<slot name="default">
<view class="tn-p-lg"> 弹框内容 </view>
</slot>
</TnPopup>
<!-- </fui-bottom-popup> -->
</template>
<style scoped lang="scss">
</style>
+21
View File
@@ -0,0 +1,21 @@
<script setup lang="ts">
import TnTitle from '@tuniao/tnui-vue3-uniapp/components/title/src/title.vue'
interface Props{
title:string;
}
const props = withDefaults(defineProps<Props>(), {
title: "默认标题"
})
</script>
<template>
<TnTitle :title="title" mode="vLine" />
</template>
<style scoped lang="scss">
</style>
+23
View File
@@ -0,0 +1,23 @@
<script setup lang="ts">
</script>
<template>
<view class="param-card">
<up-gap height="10"></up-gap>
<slot name="title"></slot>
<up-gap height="10"></up-gap>
<slot name="body"></slot>
</view>
</template>
<style scoped lang="scss">
.param-card {
border: 2rpx solid #F6F7FA;
border-radius: 5rpx;
background-color: #FFFFFF;
padding: 8rpx;
margin: 0 8rpx 8rpx 8rpx;
}
</style>
+35
View File
@@ -0,0 +1,35 @@
<script setup lang="ts">
import {ref, watch} from 'vue'
import {useAppStore} from '@/stores/appStore.ts'
import TnPopup from '@tuniao/tnui-vue3-uniapp/components/popup/src/popup.vue'
import TnCircleProgress from '@tuniao/tnui-vue3-uniapp/components/circle-progress/src/circle-progress.vue'
import {storeToRefs} from "pinia";
const {showExecuting} = storeToRefs(useAppStore())
watch(showExecuting,()=>{
console.log('showExcuting', showExecuting)
})
const progressPercent = ref(30)
</script>
<template>
<TnPopup v-model="showExecuting"
v-if="showExecuting"
width="80%" height="450"
close-btn
:overlay-closeable="false">
<view class="tn-p-lg tn"> 正在绘图中
</view>
<view class="tn-flex-center">
<TnCircleProgress :percent="progressPercent" >
<text>{{progressPercent}}%</text>
</TnCircleProgress>
</view>
</TnPopup>
</template>
<style scoped lang="scss">
</style>
@@ -0,0 +1,214 @@
<script lang="ts" setup>
import TnIcon from '@tuniao/tnui-vue3-uniapp/components/icon/src/icon.vue'
import TnPhotoAlbum from '@tuniao/tnui-vue3-uniapp/components/photo-album/src/photo-album.vue'
import TnAvatar from '@tuniao/tnui-vue3-uniapp/components/avatar/src/avatar.vue'
import TnAvatarGroup from '@tuniao/tnui-vue3-uniapp/components/avatar/src/avatar-group.vue'
import TnLazyLoad from '@tuniao/tnui-vue3-uniapp/components/lazy-load/src/lazy-load.vue'
import { graphicCardEmits, graphicCardProps } from './types'
import { useGraphicCard, useGraphicCardCustomStyle } from './composables'
const props = defineProps(graphicCardProps)
const emits = defineEmits(graphicCardEmits)
const {
viewUserAvatars,
viewUserCount,
imageCount,
previewImageHandle,
cardClickEvent,
handleAvatarClick,
handleMoreClick,
handleCommentClick,
handleHotClick,
handleLikeClick,
} = useGraphicCard(props, emits)
const {
ns,
tagClass,
tagStyle,
hotClass,
hotStyle,
commentClass,
commentStyle,
likeClass,
likeStyle,
} = useGraphicCardCustomStyle(props)
function linkType(url) {
// console.log("------------------------imageCount----------",images)
// 如果输入不是字符串,返回 2(未知类型)
if (typeof url !== 'string') return 2;
// 图片扩展名正则表达式
const imageExtensions = /\.(jpg|jpeg|png|gif|bmp|webp)$/i;
// 视频扩展名正则表达式
const videoExtensions = /\.(mp4|avi|mov|mkv|flv|wmv)$/i;
// 判断是否为图片
if (imageExtensions.test(url)) return 0; // 返回 0 表示图片
// 判断是否为视频
if (videoExtensions.test(url)) return 1; // 返回 1 表示视频
// 都不是,返回 2(未知类型)
return 2;
}
</script>
<template>
<view :class="[ns.b()]" @tap="cardClickEvent">
<!-- 简要信息 -->
<view :class="[ns.e('brief-info')]">
<view :class="[ns.e('brief-info__content')]">
<view :class="[ns.e('brief-info__avatar')]" @tap.stop="handleAvatarClick">
<!-- <image class="image" :src="avatar" mode="aspectFill" />-->
<TnAvatar v-if="avatar" :url="avatar" />
<TnAvatar v-else :size="80">{{username?.slice(0,1)}}</TnAvatar>
</view>
<view :class="[ns.e('brief-info__data')]">
<view class="title tn-text-ellipsis-1">{{ title }}</view>
<view v-if="description" class="desc tn-text-ellipsis-1">
{{ description }}
</view>
</view>
</view>
<view v-if="showMore" :class="[ns.e('brief-info__operation')]">
<slot name="briefOperation">
<view :class="[ns.em('brief-info__operation', 'more')]" @tap.stop="handleMoreClick">
<TnIcon name="more-vertical" />
</view>
</slot>
</view>
</view>
<!-- 内容容器 -->
<view :class="[ns.e('container')]">
<!-- 内容 -->
<view :class="[ns.e('content')]">
<!-- 标签和内容 -->
<view :class="[ns.e('content__tags')]">
<view v-for="(tagItem, tagIndex) in tags" :key="tagIndex" class="tag-item" :class="[tagClass]"
:style="tagStyle">
<TnIcon name="topics-fill" />
{{ tagItem }}
</view>
</view>
<view :class="[ns.e('content__data')]">
{{ content }}
</view>
</view>
<view v-if="linkType(images.slice(-1)[0]) == 0">
<!-- 图片列表 -->
<view>
<view v-if="!!imageCount" :class="[ns.e('images')]">
<!-- 一张图片 -->
<view v-if="imageCount === 1" :class="[ns.em('images', 'item'), ns.is('one')]"
@tap.stop="previewImageHandle(0)">
<TnLazyLoad mode="aspectFit" :src="images[0]" />
</view>
<!-- 两张图片 -->
<view v-if="imageCount === 2" :class="[ns.em('images', 'item'), ns.is('two')]">
<TnPhotoAlbum :data="images" :column="2" />
</view>
<!-- 三张图片 -->
<view v-if="imageCount === 3" :class="[ns.em('images', 'item'), ns.is('three')]">
<view class="image-wrapper-left">
<view class="image-container" @tap.stop="previewImageHandle(0)">
<TnLazyLoad mode="aspectFit" :src="images[0]" />
</view>
</view>
<view class="image-wrapper-right">
<view class="image-container" @tap.stop="previewImageHandle(1)">
<TnLazyLoad :src="images[1]" />
</view>
<view class="image-container" @tap.stop="previewImageHandle(2)">
<TnLazyLoad :src="images[2]" />
</view>
</view>
</view>
<!-- 四张图片 -->
<view v-if="imageCount === 4" :class="[ns.em('images', 'item'), ns.is('four')]">
<TnPhotoAlbum :data="images" :column="2" />
</view>
<TnPhotoAlbum v-if="imageCount >= 5" :data="images" />
</view>
</view>
</view>
</view>
<view v-if="linkType(images.slice(-1)[0]) == 1">
<view>
<view v-if="!!imageCount" :class="[ns.e('images')]">
<!-- 一张图片 -->
<view v-if="imageCount === 1" :class="[ns.em('images', 'item'), ns.is('one')]">
<view v-for="(item,index) in images" :key="index">
<video style="width: 300px; height: 150px; background-color:transparent;" id="myVideo"
:src="images[index]" controls></video>
</view>
</view>
<view v-else >
<video style="width: 300px; height: 150px; background-color:transparent;" id="myVideo"
:src="images.slice(-1)[0]" controls></video>
</view>
<!-- 两张图片 -->
<!-- 三张图片 -->
<!-- 四张图片 -->
</view>
</view>
</view>
<!-- 底部信息 -->
<view :class="[ns.e('bottom-info'), ns.is('no-content', !!$slots.bottomRight)]">
<view :class="[ns.e('bottom-info__left')]">
<view v-if="showHot" class="count-item-data" :class="[hotClass]" :style="hotStyle"
@tap.stop="handleHotClick">
<TnIcon :name="activeHot ? activeHotIcon : hotIcon" />
<view class="count">{{ hotCount }}</view>
</view>
<view v-if="showComment" class="count-item-data" :class="[commentClass]" :style="commentStyle"
@tap.stop="handleCommentClick">
<TnIcon :name="activeComment ? activeCommentIcon : commentIcon" />
<view class="count">{{ commentCount }}</view>
</view>
<view v-if="showLike" class="count-item-data" :class="[likeClass]" :style="likeStyle"
@tap.stop="handleLikeClick">
<TnIcon :name="activeLike ? activeLikeIcon : likeIcon" />
<view class="count">{{ likeCount }}</view>
</view>
</view>
<view v-if="(showViewUser && viewUserAvatars.length) || $slots.bottomRight"
:class="[ns.e('bottom-info__right')]">
<slot name="bottomRight">
<!-- 查看用户头像列表 -->
<view :class="[ns.e('view-user-list')]">
<TnAvatarGroup border size="sm">
<TnAvatar v-for="(viewUserAvatar, viewUserIndex) in viewUserAvatars" :key="viewUserIndex"
:url="viewUserAvatar" />
</TnAvatarGroup>
</view>
<!-- 查看用户数量 -->
<view :class="[ns.e('view-user-count')]">
{{ viewCount !== undefined ? viewCount : viewUserCount }}
</view>
</slot>
</view>
</view>
</view>
</template>
<style lang="scss" scoped>
@import './theme-chalk/index.scss';
</style>
@@ -0,0 +1,169 @@
import { computed, toRef } from 'vue'
import { useComponentColor, useNamespace } from '@tuniao/tnui-vue3-uniapp/hooks'
import type { CSSProperties } from 'vue'
import type { GraphicCardProps } from '../types'
export const useGraphicCardCustomStyle = (props: GraphicCardProps) => {
const ns = useNamespace('graphic-card')
// 解析颜色
const [tagBgColorClass, tagBgColorStyle] = useComponentColor(
toRef(props, 'tagBgColor'),
'bg'
)
const [tagTextColorClass, tagTextColorStyle] = useComponentColor(
toRef(props, 'tagTextColor'),
'text'
)
const [hotColorClass, hotColorStyle] = useComponentColor(
toRef(props, 'hotColor'),
'text'
)
const [activeHotColorClass, activeHotColorStyle] = useComponentColor(
toRef(props, 'activeHotColor'),
'text'
)
const [commentColorClass, commentColorStyle] = useComponentColor(
toRef(props, 'commentColor'),
'text'
)
const [activeCommentColorClass, activeCommentColorStyle] = useComponentColor(
toRef(props, 'activeCommentColor'),
'text'
)
const [likeColorClass, likeColorStyle] = useComponentColor(
toRef(props, 'likeColor'),
'text'
)
const [activeLikeColorClass, activeLikeColorStyle] = useComponentColor(
toRef(props, 'activeLikeColor'),
'text'
)
// 标签对应的类
const tagClass = computed<string>(() => {
const cls: string[] = []
if (tagBgColorClass.value) cls.push(tagBgColorClass.value)
if (tagTextColorClass.value) cls.push(tagTextColorClass.value)
return cls.join(' ')
})
// 标签对应的样式
const tagStyle = computed<CSSProperties>(() => {
const style: CSSProperties = {}
if (!tagBgColorClass.value) {
style.backgroundColor =
tagBgColorStyle.value || 'var(--tn-color-gray-disabled)'
}
if (tagTextColorStyle.value) {
style.color = tagTextColorStyle.value
} else if (!tagTextColorClass.value && !tagBgColorClass.value) {
style.color = 'var(--tn-text-color-primary)'
}
return style
})
// 热度对应的类
const hotClass = computed<string>(() => {
const cls: string[] = [ns.e('hot')]
if (props.activeHot) {
if (activeHotColorClass.value) cls.push(activeHotColorClass.value)
} else {
if (hotColorClass.value) cls.push(hotColorClass.value)
}
return cls.join(' ')
})
// 热度对应的样式
const hotStyle = computed<CSSProperties>(() => {
const style: CSSProperties = {}
if (props.activeHot) {
if (!activeHotColorClass.value) {
style.color = activeHotColorStyle.value || 'var(--tn-color-primary)'
}
} else {
if (!hotColorClass.value) {
style.color = hotColorStyle.value || 'var(--tn-color-gray)'
}
}
return style
})
// 评论对应的类
const commentClass = computed<string>(() => {
const cls: string[] = [ns.e('comment')]
if (props.activeComment) {
if (activeCommentColorClass.value) cls.push(activeCommentColorClass.value)
} else {
if (commentColorClass.value) cls.push(commentColorClass.value)
}
return cls.join(' ')
})
// 评论对应的样式
const commentStyle = computed<CSSProperties>(() => {
const style: CSSProperties = {}
if (props.activeComment) {
if (!activeCommentColorClass.value) {
style.color = activeCommentColorStyle.value || 'var(--tn-color-primary)'
}
} else {
if (!commentColorClass.value) {
style.color = commentColorStyle.value || 'var(--tn-color-gray)'
}
}
return style
})
// 点赞对应的类
const likeClass = computed<string>(() => {
const cls: string[] = [ns.e('like')]
if (props.activeLike) {
if (activeLikeColorClass.value) cls.push(activeLikeColorClass.value)
} else {
if (likeColorClass.value) cls.push(likeColorClass.value)
}
return cls.join(' ')
})
// 点赞对应的样式
const likeStyle = computed<CSSProperties>(() => {
const style: CSSProperties = {}
if (props.activeLike) {
if (!activeLikeColorClass.value) {
style.color = activeLikeColorStyle.value || 'var(--tn-color-red)'
}
} else {
if (!likeColorClass.value) {
style.color = likeColorStyle.value || 'var(--tn-color-gray)'
}
}
return style
})
return {
ns,
tagClass,
tagStyle,
hotClass,
hotStyle,
commentClass,
commentStyle,
likeClass,
likeStyle,
}
}
@@ -0,0 +1,5 @@
export * from './graphic-card-custom'
export * from './use-graphic-card'
@@ -0,0 +1,78 @@
import { computed, ref } from 'vue'
import { isEmptyVariableInDefault } from '@tuniao/tnui-vue3-uniapp/utils'
import type { SetupContext } from 'vue'
import type { GraphicCardEmits, GraphicCardProps } from '../types'
export const useGraphicCard = (
props: GraphicCardProps,
emits: SetupContext<GraphicCardEmits>['emit']
) => {
// 分割显示的查看用户头像列表和头像数量
const viewUserAvatars = ref<string[]>([])
const viewUserCount = ref<number>(0)
if (props.showViewUser) {
viewUserAvatars.value = props.viewUserAvatars.slice(
0,
props.maxViewUserAvatarCount
)
viewUserCount.value = props.viewUserAvatars.length
}
// 图片数量
const imageCount = computed<number>(() =>
isEmptyVariableInDefault(props?.images?.length, 0)
)
// 预览图片
const previewImageHandle = (index: number) => {
uni.previewImage({
urls: props.images,
current: index,
})
}
// 卡片点击事件
const cardClickEvent = () => {
emits('click')
}
// 用户头像点击事件
const handleAvatarClick = () => {
emits('avatar-view-click')
}
// 更多按钮点击事件
const handleMoreClick = () => {
emits('more-click')
}
// 点击热度数量
const handleHotClick = () => {
emits('hot-click')
}
// 点击评论数量
const handleCommentClick = () => {
emits('comment-click')
}
// 点击点赞数量
const handleLikeClick = () => {
emits('like-click')
}
return {
viewUserAvatars,
viewUserCount,
imageCount,
previewImageHandle,
cardClickEvent,
handleAvatarClick,
handleMoreClick,
handleHotClick,
handleCommentClick,
handleLikeClick,
}
}
@@ -0,0 +1,216 @@
@use './mixins/mixins.scss' as *;
@include b(graphic-card) {
position: relative;
width: 100%;
padding: 30rpx;
// background-color: var(--tn-color-white);
background-color: transparent;
/* 简要信息 start */
@include e(brief-info) {
display: flex;
align-items: center;
/* 简要内容 start */
&__content {
flex-grow: 1;
display: flex;
}
&__avatar {
flex-shrink: 0;
width: 90rpx;
height: 90rpx;
border-radius: 50%;
background-color: var(--tn-color-gray--disabled);
.image {
width: 100%;
height: 100%;
border-radius: inherit;
}
}
&__data {
flex-grow: 1;
margin-left: 24rpx;
color: var(--tn-text-color-primary);
line-height: 1;
padding-top: 6rpx;
.title {
font-size: 30rpx;
}
.desc {
margin-top: 16rpx;
font-size: 24rpx;
color: var(--tn-color-gray);
}
}
/* 简要内容 end */
/* 操作按钮 start */
&__operation {
flex-grow: 0;
&--more {
font-size: 40rpx;
font-weight: bold;
color: var(--tn-color-gray-disabled);
padding-left: 20rpx;
}
}
/* 操作按钮 end */
}
/* 简要信息 end */
/* 内容 start */
@include e(container) {
margin-top: 20rpx;
}
@include e(content) {
/* 内容标签 start */
&__tags {
display: flex;
float: left;
align-items: center;
margin-top: 4rpx;
.tag-item {
width: fit-content;
height: fit-content;
padding: 8rpx 16rpx;
border-radius: 6rpx;
margin-right: 16rpx;
font-size: 24rpx;
line-height: 1;
}
}
/* 内容标签 end */
/* 内容数据 start */
&__data {
font-size: 28rpx;
line-height: 1.8em;
// display: inline-block;
color: var(--tn-text-color-primary);
// 文字两端对齐
text-align: justify;
}
/* 内容数据 end */
}
/* 图片列表 start */
@include e(images) {
width: 100%;
margin-top: 20rpx;
@include m(item) {
/* 一张图片 start */
@include when(one) {
width: 70%;
height: 300rpx;
border-radius: 15rpx;
}
/* 一张图片 end */
/* 两张图片 start */
@include when(two) {
width: 80%;
}
/* 两张图片 end */
/* 三张图片 start */
@include when(three) {
position: relative;
width: 100%;
height: 332rpx;
display: flex;
align-items: center;
/* 左边单图 start */
.image-wrapper-left {
position: relative;
width: 50%;
height: 100%;
.image-container {
width: 100%;
height: 100%;
border-radius: 15rpx;
}
}
/* 左边单图 end */
/* 右边双图 start */
.image-wrapper-right {
position: relative;
flex: 1;
height: 100%;
margin-left: 28rpx;
display: flex;
flex-direction: column;
align-items: center;
.image-container {
flex: 1;
width: 100%;
border-radius: 15rpx;
& + .image-container {
margin-top: 28rpx;
}
}
}
/* 右边双图 end */
}
/* 三张图片 end */
/* 四张图片 start */
@include when(four) {
width: 80%;
}
/* 四张图片 end */
}
}
/* 图片列表 end */
/* 内容 end */
/* 底部信息 start */
@include e(bottom-info) {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 16rpx;
/* 左边查看数据 start */
&__left {
display: flex;
align-items: center;
.count-item-data {
display: flex;
align-items: center;
margin-right: 20rpx;
.count {
margin-left: 6rpx;
}
}
}
/* 左边查看数据 end */
/* 右边查看用户头像数据 start */
&__right {
display: flex;
align-items: center;
}
/* 右边查看用户头像数据 end */
@include when(no-content) {
margin-top: 24rpx;
}
}
@include e(view-user-count) {
margin-left: 8rpx;
font-size: 26rpx;
color: var(--tn-color-gray);
}
/* 底部信息 end */
}
@@ -0,0 +1,5 @@
$namespace: 'tn' !default;
$common-separator: '-' !default;
$element-separator: '__' !default;
$modifier-separator: '--' !default;
$state-prefix: 'is-' !default;
@@ -0,0 +1,105 @@
@use 'config';
// BEM support Func
// 块(Block):代表一个独立的组件或页面中的一个大型部分。块可以看作是一个命名空间,用于包含相关元素和修饰符。例如,一个导航栏可以被视为一个块。
// 元素(Element):代表块的一部分,但不能独立存在。元素总是属于一个块,并且与该块紧密相关。元素由块名称和元素名称组成,中间由双下划线(__)连接。例如,一个导航栏可以包含多个链接,链接可以被视为导航栏块的元素。
// 修饰符(Modifier):代表块或元素的变体或状态。修饰符用于修改块或元素的外观或行为。修饰符由块名称或元素名称,连字符(-)和修饰符名称组成。例如,一个导航栏可以具有活动状态或浅色主题,这些状态可以通过添加修饰符类进行实现。
@function selectorToString($selector) {
$selector: inspect($selector);
$selector: str-slice($selector, 2, -2);
@return $selector;
}
@function containsModifier($selector) {
$selector: selectorToString($selector);
@if str_index($selector, config.$modifier-separator) {
@return true;
} @else {
@return false;
}
}
@function containWhenFlag($selector) {
$selector: selectorToString($selector);
@if str-index($selector, '.' + config.$state-prefix) {
@return true;
} @else {
@return false;
}
}
@function containPseudoClass($selector) {
$selector: selectorToString($selector);
@if str-index($selector, ':') {
@return true;
} @else {
@return false;
}
}
@function hitAllSpecialNestRule($selector) {
@return containsModifier($selector) or containWhenFlag($selector) or
containPseudoClass($selector);
}
// join var name
// joinVarName(('button', 'text-color')) => '--tn-button-text-color'
@function joinVarName($list) {
$name: '--' + config.$namespace;
@each $item in $list {
@if $item != '' {
$name: $name + '-' + $item;
}
}
@return $name;
}
// getCssVarName('button', 'text-color') => '--tn-button-text-color'
@function getCssVarName($args...) {
@return joinVarName($args);
}
// getCssVar('button', 'text-color') => var(--tn-button-text-color)
@function getCssVar($args...) {
@return var(#{joinVarName($args)});
}
// getCssVarWithDefault('button', 'text-color', 'red') => var(--tn-button-text-color, red)
@function getCssVarWithDefault($args, $default) {
@return var(#{joinVarName($args)}, #{$default});
}
// bem('block', 'element', ''modifier) => 'tn-block__element--modifier'
@function bem($block, $element: '', $modifier: '') {
$name: config.$namespace + config.$common-separator + $block;
@if $element != '' {
$name: $name + config.$element-separator + $element;
}
@if $modifier != '' {
$name: $name + config.$modifier-separator + $modifier;
}
@return $name;
}
// 字符串替换
@function str-replace($string, $search, $replace) {
$index: str-index($string, $search);
@if $index {
@return str-slice($string, 1, $index - 1) + $replace +
str-replace(
str-slice($string, $index + str-length($search)),
$search,
$replace
);
}
@return $string;
}
@@ -0,0 +1,134 @@
@use 'function' as *;
// forward mixins
@forward 'config';
@forward 'function';
@use 'config' as *;
// BEM
@mixin b($block) {
$B: $namespace + '-' + $block !global;
.#{$B} {
@content;
}
}
@mixin e($element) {
$E: $element !global;
$selector: &;
$currentSelector: '';
@each $unit in $element {
$currentSelector: #{$currentSelector +
'.' +
$B +
$element-separator +
$unit +
','};
}
@if hitAllSpecialNestRule($selector) {
@at-root {
#{$selector} {
#{$currentSelector} {
@content;
}
}
}
} @else {
@at-root {
#{$currentSelector} {
@content;
}
}
}
}
@mixin m($modifier) {
$selector: &;
$currentSelector: '';
@each $unit in $modifier {
$currentSelector: #{$currentSelector +
$selector +
$modifier-separator +
$unit +
','};
}
@at-root {
#{$currentSelector} {
@content;
}
}
}
@mixin configurable-m($modifier, $E-flag: false) {
$selector: &;
$interpolation: '';
@if $E-flag {
$interpolation: $element-separator + $E-flag;
}
@at-root {
#{$selector} {
.#{$B + $interpolation + $modifier-separator + $modifier} {
@content;
}
}
}
}
@mixin spec-selector(
$specSelector: '',
$element: $E,
$modifier: false,
$block: $B
) {
$modifierCombo: '';
$elementCombo: '';
@if $modifier {
$modifierCombo: $modifier-separator + $modifier;
}
@if $element {
$elementCombo: $element-separator + $element;
}
@at-root {
#{&}#{$specSelector}.#{$block + $elementCombo + $modifierCombo} {
@content;
}
}
}
@mixin meb($modifier: false, $element: $E, $block: $B) {
$selector: &;
$modifierCombo: '';
@if $modifier {
$modifierCombo: $modifier-separator + $modifier;
}
@at-root {
#{$selector} {
.#{$block + $element-separator + $element + $modifierCombo} {
@content;
}
}
}
}
@mixin when($state) {
@at-root {
&.#{$state-prefix + $state} {
@content;
}
}
}
@mixin pseudo($pseudo) {
@at-root #{&}#{':#{$pseudo}'} {
@content;
}
}
@@ -0,0 +1,246 @@
import { buildProps, definePropType } from '@tuniao/tnui-vue3-uniapp/utils'
import type { ExtractPropTypes } from 'vue'
export const graphicCardProps = buildProps({
/**
* @description 头像地址
*/
avatar: {
type: String,
required: true,
},
/**
* @description 用户名
*/
username: {
type: String,
required: false,
},
/**
* @description 标题
*/
title: {
type: String,
required: true,
},
/**
* @description 描述
*/
description: {
type: String,
required: true,
},
/**
* @description 标签
*/
tags: {
type: definePropType<string[]>(Array),
default: () => [],
},
/**
* @description 标签背景颜色,以tn开头使用图鸟内置的颜色
*/
tagBgColor: String,
/**
* @description 标签文字颜色,以tn开头使用图鸟内置的颜色
*/
tagTextColor: String,
/**
* @description 内容
*/
content: String,
/**
* @description 图片列表
*/
images: {
type: definePropType<string[]>(Array),
default: () => [],
},
/**
* @description 是否显示更多(是否显示顶部右边操作区域)
*/
showMore: {
type: Boolean,
default: true,
},
/**
* @description 显示热度数量
*/
showHot: {
type: Boolean,
default: true,
},
/**
* @description 是否激活热度
*/
activeHot: Boolean,
/**
* @description 热度数量数据
*/
hotCount: {
type: Number,
default: 0,
},
/**
* @description 热度数量图标
*/
hotIcon: {
type: String,
default: 'rocket',
},
/**
* @description 激活时热度数量图标
*/
activeHotIcon: {
type: String,
default: 'rocket-fill',
},
/**
* @description 热度数量图标颜色
*/
hotColor: String,
/**
* @description 激活时热度数量图标颜色
*/
activeHotColor: String,
/**
* @description 显示评论数量
*/
showComment: {
type: Boolean,
default: true,
},
/**
* @description 是否激活评论
*/
activeComment: Boolean,
/**
* @description 评论数量数据
*/
commentCount: {
type: Number,
default: 0,
},
/**
* @description 评论数量图标
*/
commentIcon: {
type: String,
default: 'message',
},
/**
* @description 激活时评论数量图标
*/
activeCommentIcon: {
type: String,
default: 'message-fill',
},
/**
* @description 评论数量图标颜色
*/
commentColor: String,
/**
* @description 激活时评论数量图标颜色
*/
activeCommentColor: String,
/**
* @description 显示点赞数量
*/
showLike: {
type: Boolean,
default: true,
},
/**
* @description 是否激活点赞
*/
activeLike: Boolean,
/**
* @description 点赞数量数据
*/
likeCount: {
type: Number,
default: 0,
},
/**
* @description 点赞数量图标
*/
likeIcon: {
type: String,
default: 'like-lack',
},
/**
* @description 激活时点赞数量图标
*/
activeLikeIcon: {
type: String,
default: 'like-fill',
},
/**
* @description 点赞数量图标颜色
*/
likeColor: String,
/**
* @description 激活时点赞数量图标颜色
*/
activeLikeColor: String,
/**
* @description 显示查看用户信息
*/
showViewUser: {
type: Boolean,
default: true,
},
/**
* @description 实际查看数量数据
*/
viewCount: {
type: Number,
default: 0,
},
/**
* @description 查看用户头像列表
*/
viewUserAvatars: {
type: definePropType<string[]>(Array),
default: () => [],
},
/**
* @description 最大显示用户头像数量
*/
maxViewUserAvatarCount: {
type: Number,
default: 4,
},
})
export const graphicCardEmits = {
/**
* @description 点击图文卡片
*/
click: () => true,
/**
* @description 点击用户头像和浏览数量
*/
'avatar-view-click': () => true,
/**
* @description 更多按钮点击
*/
'more-click': () => true,
/**
* @description 点击热度数量
*/
'hot-click': () => true,
/**
* @description 点击评论数量
*/
'comment-click': () => true,
/**
* @description 点击点赞数量
*/
'like-click': () => true,
}
export type GraphicCardProps = ExtractPropTypes<typeof graphicCardProps>
export type GraphicCardEmits = typeof graphicCardEmits
+187
View File
@@ -0,0 +1,187 @@
<script setup lang="ts">
import { ref, watch, onUnmounted } from "vue";
import { uploadFile } from "@/utils/request.ts";
import MyTitle from "@/components/common/MyTitle.vue";
import ParamCard from "@/components/common/ParamCard.vue";
import { onLoad, onReady } from "@dcloudio/uni-app";
import type { IDynamicOptions } from "@/types";
interface Props {
title ?: string;
options ?: IDynamicOptions;
}
const props = withDefaults(defineProps<Props>(), {
title: "上传",
});
const modelValue = defineModel({
default: "",
});
const audioList = ref<string[]>([]);
const audioSrc = ref<string>("");
const audioName = ref<string>("默认音频");
let innerAudioContext : UniApp.InnerAudioContext | null = null;
const currentTime = ref<number>(0);
const duration = ref<number>(0);
const progress = ref<number>(0); // 初始化为 0
onReady(() => {
audioList.value = modelValue.value ? [modelValue.value] : [];
});
watch(audioList, () => {
console.log("audioList", audioList.value[0]);
modelValue.value = audioList.value[0];
if (audioList.value[0]) {
audioSrc.value = audioList.value[0];
audioName.value = audioList.value[0].split('/').pop() || "默认音频";
initInnerAudioContext();
}
});
const uploadFilePromise = async (file : UniApp.ChooseImageSuccessCallbackResultFile) => {
const url = file.path;
return new Promise(async (resolve, reject) => {
const uploadResult = await uploadFile<string>(url);
console.log("uploadResult", uploadResult);
if (uploadResult) {
resolve(uploadResult);
}
});
};
const initInnerAudioContext = () => {
if (innerAudioContext) {
innerAudioContext.destroy();
}
innerAudioContext = uni.createInnerAudioContext();
innerAudioContext.autoplay = false;
innerAudioContext.src = audioSrc.value;
innerAudioContext.onPlay(() => {
console.log('开始播放');
});
innerAudioContext.onError((res) => {
console.log(res.errMsg);
console.log(res.errCode);
});
innerAudioContext.onTimeUpdate(() => {
currentTime.value = innerAudioContext?.currentTime || 0;
duration.value = innerAudioContext?.duration || 0;
progress.value = duration.value > 0 ? (currentTime.value / duration.value) * 100 : 0;
});
};
const chooseAudioFile = () => {
wx.chooseMessageFile({
count: 1,
type: 'file',
extension: ['mp3', 'wav'], // 指定音频文件类型
success(res) {
const audioFile = res.tempFiles[0];
uploadAudioFile(audioFile);
},
fail(err) {
console.error('选择文件失败', err);
}
});
};
const uploadAudioFile = async (file : UniApp.ChooseImageSuccessCallbackResultFile) => {
try {
const uploadResult = await uploadFilePromise(file);
if (uploadResult) {
audioSrc.value = uploadResult;
audioName.value = file.name;
audioList.value = [uploadResult];
console.log('上传成功', uploadResult);
}
} catch (error) {
console.error('上传失败', error);
}
};
const buttonShow = ref(false)
const playAudio = () => {
if (innerAudioContext) {
// 确保在播放新音频之前停止当前正在播放的音频
innerAudioContext.stop();
innerAudioContext.play();
}
buttonShow.value = true
};
const pauseAudio = () => {
if (innerAudioContext) {
innerAudioContext.pause();
console.log('暂停播放');
buttonShow.value = false
}
};
onUnmounted(() => {
if (innerAudioContext) {
innerAudioContext.destroy();
}
});
const formatTime = (time: number): string => {
const minutes = Math.floor(time / 60);
const seconds = Math.floor(time % 60);
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
};
</script>
<template>
<ParamCard>
<template #title>
<MyTitle :title="title" />
</template>
<template #body>
<view class='audioCard'>
<!-- <audio style="width:90%; color: red;" :src="audioSrc" :name="audioName" controls></audio> -->
<fui-row margin-bottom="24rpx" style="width: 100%; margin-top: 15%; text-align: center;">
<fui-col :span="3">
<view style="margin-top: 75%;">
<fui-icon name="suspend" @click="playAudio" v-show="!buttonShow"></fui-icon>
<fui-icon name="play" @click="pauseAudio" v-show="buttonShow"></fui-icon>
</view>
</fui-col>
<fui-col :span="21">
<view style="margin-top: 5%; margin-bottom: 5%; text-align: left; overflow: hidden; width: 200px;white-space: nowrap; text-overflow: ellipsis;">歌名{{audioName|| }}</view>
<fui-progress :percent=" progress.toFixed(0) || 0" style="margin-bottom: 24%;"></fui-progress>
</fui-col>
</fui-row>
<view style="margin-bottom: 10%;"></view>
<fui-row margin-bottom="24rpx" style="width: 100%; text-align: right;">
<fui-col :span="24">
<view style="margin-top: -10%;"><fui-icon name="pullup" @click="chooseAudioFile"></fui-icon></view>
</fui-col>
</fui-row>
</view>
</template>
</ParamCard>
</template>
<style scoped lang="scss">
.upload-new-btn {
width: 100%;
height: 300rpx;
background-color: #f4f5f6;
border-radius: 10rpx;
}
.audioCard {
text-align: left;
}
</style>
@@ -0,0 +1,30 @@
<script setup lang="ts">
import TnNumberBox from '@tuniao/tnui-vue3-uniapp/components/number-box/src/number-box.vue'
import ParamCard from "@/components/common/ParamCard.vue";
import MyTitle from "@/components/common/MyTitle.vue";
const numberValue=defineModel({
default:1
})
</script>
<template>
<ParamCard>
<template #title>
<View style="display: flex;justify-content: space-between;">
<MyTitle title="图像批次"></MyTitle>
<TnNumberBox v-model="numberValue" />
</View>
</template>
</ParamCard>
</template>
<style scoped lang="scss">
</style>
+53
View File
@@ -0,0 +1,53 @@
<script setup lang="ts">
import { ref } from 'vue'
import MyTitle from "@/components/common/MyTitle.vue";
import ParamCard from "@/components/common/ParamCard.vue";
import type {IDynamicOptions} from "@/types";
const sliderValue = defineModel({
default: 512,
})
interface Props {
title?: string
options?:IDynamicOptions
}
const props= withDefaults(defineProps<Props>(), {
title: '选择大小',
options:{
min:512,
max:1024,
step:8
}
})
console.log(props.options,typeof props.options)
</script>
<template>
<ParamCard>
<template #title>
<MyTitle :title="title"></MyTitle>
</template>
<template #body>
<up-slider
showValue
v-model="sliderValue"
:step="options.step"
:min="options.min"
:max="options.max"></up-slider>
</template>
</ParamCard>
</template>
<style scoped lang="scss">
</style>
+18
View File
@@ -0,0 +1,18 @@
<script setup lang="ts">
import CustomSlider from "@/components/dynamic/CustomSlider.vue";
const modelValue=defineModel({
default:512
})
</script>
<template>
<CustomSlider v-model="modelValue" title="高度"/>
</template>
<style scoped lang="scss">
</style>
@@ -0,0 +1,195 @@
<template>
<ParamCard>
<template #title>
<MyTitle :title="title"></MyTitle>
</template>
<template #body>
<scroll-view scroll-y="true" class="image-preview-container">
<view class="image-grid">
<view
class="image-item"
v-for="(image, index) in images"
:key="index"
:class="{'selected': selectedIndex === index}"
@click="selectImage(index)"
>
<image
:class="{'selected': selectedIndex === index}"
:src="image.src"
mode="aspectFill" />
<view class="tn-text-xs tn-text-center tn-text-ellipsis-1">{{image.title}}</view>
<view class="selected-icon">
<TnIcon
v-if="index===selectedIndex"
name="check"/>
</view>
</view>
</view>
<!--选中的预览-->
<!-- <view class="selected-preview">-->
<!-- <img-->
<!-- :src="selectedImage"-->
<!-- alt="Selected Image"-->
<!-- class="selected-image"-->
<!-- />-->
<!-- </view>-->
</scroll-view>
</template>
</ParamCard>
</template>
<script setup lang="ts">
import {computed, ref, watch} from "vue";
import ParamCard from "@/components/common/ParamCard.vue";
import MyTitle from "@/components/common/MyTitle.vue";
import type {IDynamicOptions, IImageSelectItem} from "@/types";
import TnIcon from "@tuniao/tnui-vue3-uniapp/components/icon/src/icon.vue";
import {onLoad} from "@dcloudio/uni-app";
interface Props {
title?:string
options?:IDynamicOptions
}
const props=withDefaults(defineProps<Props>(),{
title:'选择风格'
})
onLoad(()=>{
// 兼容原有的设定,options是数组
// console.log("props",typeof JSON.parse(props.options));
// if(Array.isArray(JSON.parse(props.options))){
// props.options.imageSelectItems=JSON.parse(props.options);
// console.log("props.options",props.options);
// }
})
const selectedValue = defineModel({default:null});
const selectedIndex = ref(0);
watch(selectedIndex,()=>{
selectedValue.value=images.value[selectedIndex.value].value
})
const images = computed<IImageSelectItem[]>(()=>{
if(!props.options) return [];
//兼容原来的预览图像设定
if(typeof props.options==='string' && Array.isArray(JSON.parse(props.options))){
return JSON.parse(props.options) as IImageSelectItem[];
}
if(Array.isArray(props.options)){
return props.options as IImageSelectItem[];
}
// 新的数据格式
const options = props.options as IDynamicOptions;
if(options.imageSelectItems){
return options.imageSelectItems
}else{
return [];
}
})
// const images = ref([
// 'https://wangbo0808.oss-cn-shanghai.aliyuncs.com/aidraw/image/temps/1718244224901.png',
// 'https://oss.gptpro.ink/temps/image/dW2Et6-0001.png',
// 'https://wangbo0808.oss-cn-shanghai.aliyuncs.com/aidraw/image/temps/1718244224901.png',
// 'https://oss.gptpro.ink/temps/image/dW2Et6-0001.png',
// 'https://oss.gptpro.ink/temps/image/1725323142925.png',
// 'https://wangbo0808.oss-cn-shanghai.aliyuncs.com/aidraw/image/temps/1718244224901.png',
// 'https://wangbo0808.oss-cn-shanghai.aliyuncs.com/aidraw/image/temps/1718244224901.png',
// 'https://oss.gptpro.ink/temps/image/1725323142925.png',
// 'https://oss.gptpro.ink/temps/image/1725284691901.png',
// 'https://wangbo0808.oss-cn-shanghai.aliyuncs.com/aidraw/image/temps/1718244224901.png',
// 'https://oss.gptpro.ink/temps/image/1725284691901.png',
// 'https://wangbo0808.oss-cn-shanghai.aliyuncs.com/aidraw/image/temps/1718244224901.png',
// 'https://wangbo0808.oss-cn-shanghai.aliyuncs.com/aidraw/image/temps/1718244224901.png',
// 'https://wangbo0808.oss-cn-shanghai.aliyuncs.com/aidraw/image/temps/1718244224901.png',
// 'https://wangbo0808.oss-cn-shanghai.aliyuncs.com/aidraw/image/temps/1718244224901.png',
// 'https://wangbo0808.oss-cn-shanghai.aliyuncs.com/aidraw/image/temps/1718244224901.png',
// 'https://wangbo0808.oss-cn-shanghai.aliyuncs.com/aidraw/image/temps/1718244224901.png',
// 'https://wangbo0808.oss-cn-shanghai.aliyuncs.com/aidraw/image/temps/1718244224901.png',
// ]);
const selectedImage=computed(()=>{
return images.value[selectedIndex.value];
})
const selectImage=(index)=> {
selectedIndex.value = index;
}
</script>
<style scoped lang="scss">
.image-preview-container {
min-height: 260rpx;
max-height: 400rpx;
padding: 10rpx;
}
.image-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20rpx;
}
.image-item {
position: relative;
overflow: hidden;
cursor: pointer;
border-radius: 10px;
}
.image-item image.selected {
box-shadow: 4px 4px 10rpx rgba(2, 22, 37, 0.5);
//放大
transform: scale(1.05);
}
//遮罩 暂时没用
.image-item.selected::after {
content: '';
position: absolute;
bottom: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.2);
}
.image-item image {
width: 100%;
height: 160rpx;
transition: transform 0.3s;
//父元素中居中
margin: auto;
}
.selected-preview {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
}
.selected-image {
max-width: 100%;
border-radius: 10px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
}
.selected-icon{
position: absolute;
top: 0;
right: 0;
padding: 0 0 5rpx 5rpx;
background-color: $u-primary;
color: white;
}
</style>
+102
View File
@@ -0,0 +1,102 @@
<script setup lang="ts">
import TnImageUpload from "@tuniao/tnui-vue3-uniapp/components/image-upload/src/image-upload.vue";
import type {ImageUploadCustomFunction, ImageUploadFile,TnImageUploadInstance} from "@tuniao/tnui-vue3-uniapp";
import {ref, watch} from "vue";
import { uploadFile} from "@/utils/request.ts";
import MyTitle from "@/components/common/MyTitle.vue";
import ParamCard from "@/components/common/ParamCard.vue";
import {onLoad, onReady} from "@dcloudio/uni-app";
import type {IDynamicOptions} from "@/types";
interface Props{
title?:string
options?:IDynamicOptions
}
const props = withDefaults(defineProps<Props>(), {
title: "上传",
})
const modelValue =defineModel({
default:""
})
const imageList = ref<string[]>([])
onReady(()=>{
imageList.value=modelValue.value?[modelValue.value]:[]
})
watch(imageList,()=>{
console.log("imageList",imageList)
for(const item of imageList.value){
modelValue.value=item
}
modelValue.value=imageList.value[0]
})
const uploadFilePromise: ImageUploadCustomFunction = async (file: ImageUploadFile) => {
const url = (file as UniApp.ChooseImageSuccessCallbackResultFile).path
return new Promise(async (resolve, reject) => {
const uploadResult= await uploadFile<string>(url)
console.log("uploadResult",uploadResult)
if(uploadResult){
resolve(uploadResult)
}
})
};
const imageUploadRef = ref<TnImageUploadInstance>()
const chooseFile = () => {
imageUploadRef.value?.chooseFile()
}
</script>
<template>
<ParamCard>
<template #title>
<MyTitle :title="title"/>
</template>
<template #body>
<TnImageUpload
ref="imageUploadRef"
v-model="imageList"
:limit="1"
:custom-upload-handler="uploadFilePromise">
<!-- <template #uploadBtn>
<view class="upload-new-btn tn-flex-center tn-flex-column" @tap.stop="chooseFile">
<tn-icon name="upload" size="40"></tn-icon>
请上传图片
</view>
</template>
<template #uploadImage="{ data }">
<view class="tn-flex-center" style="max-height: 260px">
<image
class="tn-flex-center-center"
:src="data.url"
mode="widthFix"
/>
</view>
</template> -->
</TnImageUpload>
<!-- <fui-upload ref="imageUploadRef" v-model="imageList" @success="success" @error="error" @complete="complete"></fui-upload> -->
</template>
</ParamCard>
</template>
<style scoped lang="scss">
.upload-new-btn{
width: 100%;
height: 300rpx;
background-color: #f4f5f6;
border-radius: 10rpx;
}
</style>
+48
View File
@@ -0,0 +1,48 @@
<script setup lang="ts">
import Picker from "@/components/dynamic/Picker.vue";
import { ref } from "vue";
import {getModelListByWorkflowId} from "@/composables/useWorkFlow.ts";
import type {IDynamicOptions} from "@/types";
interface Props{
title?:string;
workflow_id:string
options?:IDynamicOptions
}
const props = withDefaults(defineProps<Props>(),{
workflow_id:"",
title:"选择大模型",
options:{}
})
const selectValue=defineModel()
const modelList =ref<string[]>([])
const handleInitData=async ()=>{
if(!props.workflow_id){
return
}
const result = await getModelListByWorkflowId(props.workflow_id)
if(result){
modelList.value=result
}
}
handleInitData()
</script>
<template>
<Picker
:title="title"
v-model="selectValue"
:options="modelList"/>
</template>
<style scoped lang="scss">
</style>
+114
View File
@@ -0,0 +1,114 @@
<script setup lang="ts">
import TnImageUpload from "@tuniao/tnui-vue3-uniapp/components/image-upload/src/image-upload.vue";
import type {ImageUploadCustomFunction, ImageUploadFile,TnImageUploadInstance} from "@tuniao/tnui-vue3-uniapp";
import {ref, watch} from "vue";
import { uploadFile} from "@/utils/request.ts";
import MyTitle from "@/components/common/MyTitle.vue";
import ParamCard from "@/components/common/ParamCard.vue";
import {onLoad, onReady} from "@dcloudio/uni-app";
import type {IDynamicOptions} from "@/types";
interface Props{
title?:string
options?:IDynamicOptions
}
const props = withDefaults(defineProps<Props>(), {
title: "上传",
})
const modelValue =defineModel({
default: () => []
})
const imageList = ref<string[]>([])
onReady(()=>{
imageList.value=modelValue.value||[]
})
watch(imageList,()=>{
console.log("imageList",imageList)
modelValue.value = imageList.value
})
/**
* `uploadFilePromise` 是一个自定义的图片上传函数,用于处理图片上传逻辑。
* 它接收一个 `ImageUploadFile` 类型的文件对象,返回一个 Promise 对象,
* 该 Promise 在上传成功时解析为上传结果,在上传失败时被拒绝。
*
* @param {ImageUploadFile} file - 需要上传的图片文件对象
* @returns {Promise<string>} 一个 Promise 对象,成功时返回上传结果字符串
*/
const uploadFilePromise: ImageUploadCustomFunction = async (file: ImageUploadFile) => {
const url = (file as UniApp.ChooseImageSuccessCallbackResultFile).path
return new Promise(async (resolve, reject) => {
const uploadResult= await uploadFile<string>(url)
console.log("uploadResult",uploadResult)
if(uploadResult){
resolve(uploadResult)
}
})
};
const imageUploadRef = ref<TnImageUploadInstance>()
/**
* 触发图片选择操作。
* 通过引用 `imageUploadRef` 调用其 `chooseFile` 方法来打开文件选择器,
* 允许用户选择要上传的图片文件。
*/
const chooseFile = () => {
imageUploadRef.value?.chooseFile()
}
</script>
<template>
<ParamCard>
<template #title>
<MyTitle :title="title"/>
</template>
<template #body>
<TnImageUpload
ref="imageUploadRef"
v-model="imageList"
:limit="5"
:custom-upload-handler="uploadFilePromise">
<!-- <template #uploadBtn>
<view class="upload-new-btn tn-flex-center tn-flex-column" @tap.stop="chooseFile">
<tn-icon name="upload" size="40"></tn-icon>
请上传图片
</view>
</template>
<template #uploadImage="{ data }">
<view class="tn-flex-center" style="max-height: 260px">
<image
class="tn-flex-center-center"
:src="data.url"
mode="widthFix"
/>
</view>
</template> -->
</TnImageUpload>
<!-- <fui-upload ref="imageUploadRef" v-model="imageList" @success="success" @error="error" @complete="complete"></fui-upload> -->
</template>
</ParamCard>
</template>
<style scoped lang="scss">
.upload-new-btn{
width: 100%;
height: 300rpx;
background-color: #f4f5f6;
border-radius: 10rpx;
}
</style>
+99
View File
@@ -0,0 +1,99 @@
<script setup lang="ts">
import {ref, reactive, computed} from 'vue';
import ParamCard from "@/components/common/ParamCard.vue";
import MyTitle from "@/components/common/MyTitle.vue";
import TnInput from '@tuniao/tnui-vue3-uniapp/components/input/src/input.vue'
import TnPicker from '@tuniao/tnui-vue3-uniapp/components/picker/src/picker.vue'
import type {IDynamicOptions} from "@/types";
const show = ref(false);
interface Props{
title?:string
options?:IDynamicOptions | any[]
}
const props = withDefaults(defineProps<Props>(),{
title:'请选择',
options:{
selectItems:[]
} as IDynamicOptions
})
/** picker的数据 */
const pickerData = computed(()=> {
if (!props.options) {
return []
}
//兼容直接拆传入数组
if (Array.isArray(props.options)){
//兼容早期设定,传入的是title,value格式
if(props.options[0] && props.options[0].title){
return props.options.map(item=>({label:item.title,value:item.value}))
}
if(props.options[0] && (typeof props.options[0])!=='object'){
//直接传入字符串数组
return props.options.map(item=>({label:item,value:item}))
}
return props.options
}
//传入items
if(props.options.selectItems){
return props.options.selectItems
}
return []
})
const selected = defineModel()
const handleConfirm=(item: any)=> {
console.log('confirm',item)
if(!item)return
selected.value = item;
show.value = false;
}
const handleCancel=(value: any)=> {
show.value = false;
}
const inputValue = computed(()=>{
return pickerData.value.find(item =>item.value===selected.value)?.label || ''
})
</script>
<template>
<ParamCard>
<template #title>
<MyTitle :title="title"></MyTitle>
</template>
<template #body>
<view style="display: flex;justify-content: start;align-items: start;">
<TnInput
v-model="inputValue"
type="select"
style="width: 100%"
placeholder="请选择"
@click="show=true"
/>
</view>
<TnPicker
v-model="selected"
v-model:open="show"
@cancel="handleCancel"
:data="pickerData"
@confirm="handleConfirm"
/>
<!-- <up-picker-->
<!-- :show="show"-->
<!-- :columns="columns"-->
<!-- @cancel="handleCancel"-->
<!-- @confirm="handleConfirm"></up-picker>-->
</template>
</ParamCard>
</template>
<style scoped lang="scss">
</style>
+32
View File
@@ -0,0 +1,32 @@
<script setup lang="ts">
import ParamCard from "@/components/common/ParamCard.vue";
import MyTitle from "@/components/common/MyTitle.vue";
import TnInput from '@tuniao/tnui-vue3-uniapp/components/input/src/input.vue'
const inputValue=defineModel({
default: ''
})
</script>
<template>
<ParamCard>
<template #title>
<MyTitle title="提示词"/>
</template>
<template #body>
<TnInput
height="150"
v-model="inputValue"
type="textarea"
clearable
placeholder="请输入内容" />
</template>
</ParamCard>
</template>
<style scoped lang="scss">
</style>
+70
View File
@@ -0,0 +1,70 @@
<script setup lang="ts">
import TnInput from '@tuniao/tnui-vue3-uniapp/components/input/src/input.vue'
import ParamCard from "@/components/common/ParamCard.vue";
import MyTitle from "@/components/common/MyTitle.vue";
import TnIcon from '@tuniao/tnui-vue3-uniapp/components/icon/src/icon.vue'
import type {IDynamicOptions} from "@/types";
import {generateRandomNumber} from "@/utils/common.ts";
import {onLoad, onReady} from "@dcloudio/uni-app";
const inputValue = defineModel({
default: 0
})
interface Props {
title?: string
options?: IDynamicOptions
}
const props = withDefaults(defineProps<Props>(), {
title: '随机种子',
options: {}
})
const getSeed = () => {
inputValue.value = generateRandomNumber(15)
}
onLoad(() => {
console.log('Seed page onLoad')
if (!inputValue.value || inputValue.value === 0) {
//没有默认值或者默认值为0,才重新获取种子
getSeed()
}
})
onReady(()=>{
console.log('Seed page onReady')
if (!inputValue.value || inputValue.value === 0) {
//没有默认值或者默认值为0,才重新获取种子
getSeed()
}
})
defineExpose({
getSeed
})
</script>
<template>
<ParamCard>
<template #title>
<MyTitle :title="title"></MyTitle>
</template>
<template #body>
<TnInput
type="number"
v-model="inputValue" placeholder="请输入用户名">
<template #suffix>
<TnIcon name="cube" @click="getSeed"/>
</template>
</TnInput>
</template>
</ParamCard>
</template>
<style scoped lang="scss">
</style>
+38
View File
@@ -0,0 +1,38 @@
<script setup lang="ts">
import CustomSlider from "@/components/dynamic/CustomSlider.vue";
import type {IDynamicOptions} from "@/types";
interface Props {
title?: string
options?:IDynamicOptions
}
const props= withDefaults(defineProps<Props>(), {
title: '宽度',
options:{
min:512,
max:1024,
step:8
}
})
const modelValue =defineModel({
default:512
})
console.log(111111,props.options)
</script>
<template>
<CustomSlider
v-model="modelValue"
:title="title"
:options="options"/>
</template>
<style scoped lang="scss">
</style>
@@ -0,0 +1,63 @@
<template>
<!--本文件由FirstUI授权予佛山市航电梦联网络科技有限公司会员ID 11 27营业执照号 91 4 40 605 M A 556H 1 KX H专用请尊重知识产权勿私下传播违者追究法律责任-->
<view class="fui-background__image-wrap"
:style="{position:absolute?'absolute':'fixed',background:background,zIndex:zIndex}">
<image :src="src" class="fui-background__image" :mode="aspectFill?'aspectFill':'scaleToFill'" v-if="src!=''">
</image>
<slot></slot>
</view>
</template>
<script>
export default {
name: "fui-background-image",
props: {
src: {
type: String,
default: ''
},
background: {
type: String,
default: 'transparent'
},
zIndex: {
type: [Number, String],
default: -1
},
aspectFill: {
type: Boolean,
default: true
},
absolute: {
type: Boolean,
default: false
}
}
}
</script>
<style>
.fui-background__image-wrap {
/* #ifndef APP-NVUE */
width: 100%;
height: 100%;
/* #endif */
left: 0;
top: 0;
right: 0;
bottom: 0;
}
.fui-background__image {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
/* #ifndef APP-NVUE */
width: 100%;
height: 100%;
display: block;
/* #endif */
}
</style>
@@ -0,0 +1,628 @@
<template>
<!--本文件由FirstUI授权予佛山市航电梦联网络科技有限公司会员ID 112 7营业执照号9 1 44 0 605MA5 5 6 H 1 K XH专用请尊重知识产权勿私下传播违者追究法律责任-->
<view class="fui-button__wrap"
:class="[!getWidth || getWidth==='100%' || getWidth===true?'fui-button__flex-1':'',disabled && !disabledBackground ? 'fui-button__opacity' : '']"
:style="{width: getWidth,height: getHeight,marginTop:margin[0] || 0, marginRight:margin[1]||0,marginBottom:margin[2] || margin[0]||0,marginLeft:margin[3] || margin[1]||0,borderRadius: getRadius,background:getBackground}"
@touchstart="handleStart" @touchend="handleClick" @touchcancel="handleEnd">
<button class="fui-button" :class="[
bold ? 'fui-text__bold' : '',
time && (plain || type==='link') ? 'fui-button__opacity' : '',
!background && !disabledBackground && !plain?('fui-button__'+type):'',
!getWidth || getWidth==='100%' || getWidth===true?'fui-button__flex-1':'',
time && !plain && type!=='link' ? 'fui-button__active' : '',
pc && !disabled?(plain || type==='link'?'fui-button__opacity-pc':'fui-button__active-pc'):'',
]" :style="{
width: getWidth,
height: getHeight,
lineHeight: getHeight,
background: disabled ? (disabledBackground || getTypeColor) : (plain ? 'transparent' : getBackground),
borderWidth:!borderColor || !isNvue?'0':borderWidth,
borderColor: borderColor ? borderColor : disabled && disabledBackground ? disabledBackground : (background || 'transparent'),
borderRadius: getRadius,
fontSize: getSize,
color: getColor
}" :loading="loading" :form-type="formType" :open-type="openType" :app-parameter="appParameter"
:hoverStopPropagation="hoverStopPropagation" :lang="lang" :sessionFrom="sessionFrom"
:sendMessageTitle="sendMessageTitle" :sendMessagePath="sendMessagePath" :sendMessageImg="sendMessageImg"
:showMessageCard="showMessageCard" :groupId="groupId" :guildId="guildId" :publicId="publicId"
:dataImId="dataImId" :dataImType="dataImType" :dataGoodsId="dataGoodsId" :dataOrderId="dataOrderId"
:dataBizLine="dataBizLine" :phoneNumberNoQuotaToast="phoneNumberNoQuotaToast" @getuserinfo="bindgetuserinfo"
@getphonenumber="bindgetphonenumber" @contact="bindcontact" @error="binderror"
@opensetting="bindopensetting" @chooseavatar="bindchooseavatar" @launchapp="bindlaunchapp"
@agreeprivacyauthorization="agreeprivacyauthorization" @addgroupapp="addgroupapp"
@chooseaddress="chooseaddress" @chooseinvoicetitle="chooseinvoicetitle" @subscribe="bindsubscribe"
@login="bindlogin" @im="bindim" :disabled="disabled" :scope="scope" @tap.stop="handleTap">
<text class="fui-button__text"
:class="{'fui-btn__gray-color':!background && !disabledBackground && !plain && type==='gray' && color==='#fff','fui-text__bold':bold}"
v-if="text" :style="{fontSize: getSize,lineHeight:getSize,color:getColor}">{{text}}</text>
<slot></slot>
</button>
<!-- #ifndef APP-NVUE -->
<view v-if="borderColor" class="fui-button__thin-border"
:class="[time && (plain || type==='link') && !disabled ? 'fui-button__opacity' : '',disabled && !disabledBackground ? 'fui-button__opacity' : '']"
:style="{borderWidth:borderWidth,borderColor:borderColor ? borderColor : disabled && disabledBackground ? disabledBackground : (background || 'transparent'),borderRadius: getBorderRadius}">
</view>
<!-- #endif -->
</view>
</template>
<script>
export default {
name: 'fui-button',
emits: ['click', 'getuserinfo', 'contact', 'getphonenumber', 'error', 'opensetting', 'chooseavatar', 'launchapp',
'agreeprivacyauthorization', 'addgroupapp', 'chooseaddress', 'chooseinvoicetitle', 'subscribe', 'login',
'im'
],
// #ifdef MP-WEIXIN
behaviors: ['wx://form-field-button'],
// #endif
// #ifdef MP-BAIDU
behaviors: ['swan://form-field'],
// #endif
// #ifdef MP-QQ
behaviors: ['qq://form-field'],
// #endif
// #ifdef H5
behaviors: ['uni://form-field'],
// #endif
props: {
//样式类型:primarysuccess warningdangerlinkpurplegray
type: {
type: String,
default: 'primary'
},
//按钮背景色,当传入值时type失效
background: {
type: String,
default: ''
},
//按钮显示文本
text: {
type: String,
default: ''
},
//按钮字体颜色
color: {
type: String,
default: ''
},
//按钮禁用背景色
disabledBackground: {
type: String,
default: ''
},
//按钮禁用字体颜色
disabledColor: {
type: String,
default: ''
},
// #ifdef APP-NVUE
borderWidth: {
type: String,
default: '0.5px'
},
// #endif
// #ifndef APP-NVUE
borderWidth: {
type: String,
default: '1px'
},
// #endif
borderColor: {
type: String,
default: ''
},
//V1.9.8+ 按钮大小,优先级高于width和heightmedium、small、mini
btnSize: {
type: String,
default: ''
},
//宽度
width: {
type: String,
default: '100%'
},
//高度
height: {
type: String,
default: ''
},
//字体大小,单位rpx
size: {
type: [Number, String],
default: 0
},
bold: {
type: Boolean,
default: false
},
//['20rpx','30rpx','20rpx','30rpx']->[上,右,下,左]
margin: {
type: Array,
default () {
return ['0', '0']
}
},
//圆角
radius: {
type: String,
default: ''
},
plain: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
loading: {
type: Boolean,
default: false
},
formType: {
type: String,
default: ''
},
openType: {
type: String,
default: ''
},
//支付宝小程序
//当 open-type 为 getAuthorize 时,可以设置 scope 为:phoneNumber、userInfo
scope: {
type: String,
default: ''
},
appParameter: {
type: String,
default: ''
},
//v2.3.0+
hoverStopPropagation: {
type: Boolean,
default: false
},
lang: {
type: String,
default: 'en'
},
sessionFrom: {
type: String,
default: ''
},
sendMessageTitle: {
type: String,
default: ''
},
sendMessagePath: {
type: String,
default: ''
},
sendMessageImg: {
type: String,
default: ''
},
showMessageCard: {
type: Boolean,
default: false
},
phoneNumberNoQuotaToast: {
type: Boolean,
default: true
},
groupId: {
type: String,
default: ''
},
guildId: {
type: String,
default: ''
},
publicId: {
type: String,
default: ''
},
dataImId: {
type: String,
default: ''
},
dataImType: {
type: String,
default: ''
},
dataGoodsId: {
type: String,
default: ''
},
dataOrderId: {
type: String,
default: ''
},
dataBizLine: {
type: String,
default: ''
},
index: {
type: [Number, String],
default: 0
}
},
computed: {
getTypeColor() {
// #ifndef APP-NVUE
return '';
// #endif
const app = uni && uni.$fui && uni.$fui.color
let colors = {
primary: (app && app.primary) || '#465CFF',
success: (app && app.success) || '#09BE4F',
warning: (app && app.warning) || '#FFB703',
danger: (app && app.danger) || '#FF2B2B',
link: 'transparent',
purple: (app && app.purple) || '#6831FF',
gray: '#F8F8F8'
}
return colors[this.type] || 'transparent'
},
getBackground() {
let color = this.getTypeColor
if (this.disabled || this.plain) {
color = 'transparent';
}
if (!this.disabled && !this.plain && this.background) {
color = this.background
}
return color
},
getColor() {
let color = '#fff'
if (this.color) {
color = this.disabled && this.disabledBackground ? this.disabledColor : this.color
} else {
if (this.disabled && this.disabledBackground) {
color = this.disabledColor || '#FFFFFF'
} else {
const app = uni && uni.$fui && uni.$fui.color;
const primary = (app && app.primary) || '#465CFF';
color = this.type === 'gray' ? primary : '#FFFFFF'
}
}
return color;
},
getSize() {
let size = this.size || (uni && uni.$fui && uni.$fui.fuiButton && uni.$fui.fuiButton.size) || 32
if (this.btnSize === 'small') {
size = size > 28 ? 28 : size;
} else if (this.btnSize === 'mini') {
size = size > 28 ? 24 : size;
}
return `${size}rpx`
},
getWidth() {
//medium 400*84 / small 200*84/ mini 120 * 64
let width = this.width;
if (this.btnSize && this.btnSize !== true) {
width = {
'medium': '400rpx',
'small': '200rpx',
'mini': '120rpx'
} [this.btnSize] || width
}
return width
},
getHeight() {
let height = this.height || (uni && uni.$fui && uni.$fui.fuiButton && uni.$fui.fuiButton.height) || '96rpx'
if (this.btnSize && this.btnSize !== true) {
height = {
'medium': '84rpx',
'small': '72rpx',
'mini': '64rpx'
} [this.btnSize] || height
}
return height
},
// #ifndef APP-NVUE
getBorderRadius() {
let radius = (uni && uni.$fui && uni.$fui.fuiButton && uni.$fui.fuiButton.radius) || '16rpx'
radius = this.radius || radius || '0'
if (~radius.indexOf('rpx')) {
radius = (Number(radius.replace('rpx', '')) * 2) + 'rpx'
} else if (~radius.indexOf('px')) {
radius = (Number(radius.replace('px', '')) * 2) + 'px'
} else if (~radius.indexOf('%')) {
radius = (Number(radius.replace('%', '')) * 2) + '%'
}
return radius
},
// #endif
getRadius() {
const radius = (uni && uni.$fui && uni.$fui.fuiButton && uni.$fui.fuiButton.radius) || '16rpx'
return this.radius || radius
}
},
data() {
let isNvue = false
// #ifdef APP-NVUE
isNvue = true
// #endif
return {
isNvue: isNvue,
time: 0,
trigger: false,
pc: false
};
},
created() {
// #ifdef H5
this.pc = this.isPC()
// #endif
},
methods: {
handleStart(e) {
// #ifndef APP-NVUE
if (this.disabled) return;
this.trigger = false;
if (new Date().getTime() - this.time <= 150) return;
this.trigger = true;
this.time = new Date().getTime();
// #endif
},
handleClick() {
if (this.disabled || !this.trigger) return;
this.time = 0;
},
// #ifdef H5
isPC() {
if (typeof navigator !== 'object') return false;
var userAgentInfo = navigator.userAgent;
var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
var flag = true;
for (var v = 0; v < Agents.length - 1; v++) {
if (userAgentInfo.indexOf(Agents[v]) > 0) {
flag = false;
break;
}
}
return flag;
},
// #endif
handleTap() {
if (this.disabled) return;
this.$emit('click', {
index: Number(this.index)
});
},
handleEnd(e) {
// #ifndef APP-NVUE
if (this.disabled) return;
setTimeout(() => {
this.time = 0;
}, 150);
// #endif
},
bindgetuserinfo({
detail = {}
} = {}) {
this.$emit('getuserinfo', detail);
},
bindcontact({
detail = {}
} = {}) {
this.$emit('contact', detail);
},
bindgetphonenumber({
detail = {}
} = {}) {
this.$emit('getphonenumber', detail);
},
binderror({
detail = {}
} = {}) {
this.$emit('error', detail);
},
bindopensetting({
detail = {}
} = {}) {
this.$emit('opensetting', detail);
},
bindchooseavatar({
detail = {}
} = {}) {
this.$emit('chooseavatar', detail);
},
bindlaunchapp({
detail = {}
} = {}) {
this.$emit('launchapp', detail);
},
//v2.3.0+
agreeprivacyauthorization(e) {
this.$emit('agreeprivacyauthorization', e);
},
addgroupapp(e) {
this.$emit('addgroupapp', e);
},
chooseaddress(e) {
this.$emit('chooseaddress', e);
},
chooseinvoicetitle(e) {
this.$emit('chooseinvoicetitle', e);
},
bindsubscribe(e) {
this.$emit('subscribe', e);
},
bindlogin(e) {
this.$emit('login', e);
},
bindim(e) {
this.$emit('im', e);
}
}
};
</script>
<style scoped>
.fui-button__wrap {
position: relative;
/* #ifndef APP-NVUE */
background: transparent !important;
flex-direction: row;
/* #endif */
}
.fui-button {
border-width: 0;
/* #ifndef APP-NVUE */
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
/* #endif */
border-style: solid;
position: relative;
padding-left: 0;
padding-right: 0;
/* #ifndef APP-NVUE */
overflow: hidden;
transform: translateZ(0);
-webkit-touch-callout: none;
-webkit-user-select: none;
user-select: none;
/* #endif */
}
/* #ifndef APP-NVUE */
.fui-button__thin-border {
position: absolute;
width: 200%;
height: 200%;
transform-origin: 0 0;
transform: scale(0.5, 0.5) translateZ(0);
box-sizing: border-box;
left: 0;
top: 0;
border-radius: 32rpx;
border-style: solid;
pointer-events: none;
}
/* #endif */
.fui-button__flex-1 {
flex: 1;
/* #ifndef APP-NVUE */
width: 100%;
/* #endif */
}
.fui-button::after {
border: 0;
}
/* #ifdef H5 */
.fui-button__active-pc {
position: relative;
}
.fui-button__opacity-pc:active {
opacity: 0.5;
}
.fui-button__active-pc:active::after {
content: ' ';
background-color: var(--fui-bg-color-hover, rgba(0, 0, 0, 0.2));
position: absolute;
width: 100%;
height: 100%;
left: 0;
right: 0;
top: 0;
transform: none;
z-index: 2;
border-radius: 0;
}
/* #endif */
/* #ifndef APP-NVUE */
.fui-button__active {
overflow: hidden !important;
}
.fui-button__active::after {
content: ' ';
background-color: var(--fui-bg-color-hover, rgba(0, 0, 0, 0.2));
position: absolute;
width: 100%;
height: 100%;
left: 0;
right: 0;
top: 0;
transform: none;
z-index: 2;
border-radius: 0;
}
/* #endif */
.fui-button__text {
text-align: center;
flex-direction: row;
align-items: center;
justify-content: center !important;
padding-left: 0 !important;
}
.fui-button__opacity {
opacity: 0.5;
}
.fui-text__bold {
font-weight: bold;
}
.fui-button__link {
border-color: transparent !important;
background-color: transparent !important;
}
/* #ifndef APP-NVUE */
.fui-button__primary {
border-color: var(--fui-color-primary, #465CFF) !important;
background: var(--fui-color-primary, #465CFF) !important;
}
.fui-button__success {
border-color: var(--fui-color-success, #09BE4F) !important;
background: var(--fui-color-success, #09BE4F) !important;
}
.fui-button__warning {
border-color: var(--fui-color-warning, #FFB703) !important;
background: var(--fui-color-warning, #FFB703) !important;
}
.fui-button__danger {
border-color: var(--fui-color-danger, #FF2B2B) !important;
background: var(--fui-color-danger, #FF2B2B) !important;
}
.fui-button__purple {
border-color: var(--fui-color-purple, #6831FF) !important;
background: var(--fui-color-purple, #6831FF) !important;
}
.fui-button__gray {
border-color: var(--fui-bg-color-content, #F8F8F8) !important;
background: var(--fui-bg-color-content, #F8F8F8) !important;
color: var(--fui-color-primary, #465CFF) !important;
}
.fui-btn__gray-color {
color: var(--fui-color-primary, #465CFF) !important;
}
/* #endif */
</style>
File diff suppressed because one or more lines are too long
@@ -0,0 +1,52 @@
// 本文件由FirstUI授权予佛山市航电梦联网络科技有限公司(会员ID:112 7,营业执照号: 91 440 605M A55 6 H 1 KXH)专用,请尊重知识产权,勿私下传播,违者追究法律责任。
/*!
* 剪贴板
*
* 官网地址:https://firstui.cn/
* 文档地址:https://doc.firstui.cn/
*/
// #ifdef H5
import ClipboardJS from "./clipboard.min.js"
// #endif
/**
* data 需要复制的数据
* callback 回调
* e 当用户点击后需要先请求接口再进行复制时,需要传入此参数(H5端)
* **/
const getClipboardData = function(data, callback, e) {
// #ifdef APP-PLUS || MP
uni.setClipboardData({
data: data,
success(res) {
("function" == typeof callback) && callback(true)
},
fail(res) {
("function" == typeof callback) && callback(false)
}
})
// #endif
// #ifdef H5
let event =window.event || e || {}
let clipboard = new ClipboardJS("", {
text: () => data
})
clipboard.on('success', (e) => {
("function" == typeof callback) && callback(true)
clipboard.off('success')
clipboard.off('error')
clipboard.destroy()
});
clipboard.on('error', (e) => {
("function" == typeof callback) && callback(false)
clipboard.off('success')
clipboard.off('error')
clipboard.destroy()
});
clipboard.onClick(event)
// #endif
}
export default {
getClipboardData: getClipboardData
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,360 @@
<template>
<!--本文件由FirstUI授权予佛山市航电梦联网络科技有限公司会员ID1 12 7营业执照号9 1 4 4 0 6 05M A 5 5 6 H 1 KXH专用请尊重知识产权勿私下传播违者追究法律责任-->
<view class="fui-collapse__item" :style="{marginTop:marginTop+'rpx',marginBottom:marginBottom+'rpx'}">
<view @tap.stop="onClick(!isOpen)" class="fui-collapse-item__title" :class="{'fui-collapse__disabled':disabled}"
:style="{background:background}">
<view class="fui-collapse__title">
<slot></slot>
</view>
<view v-if="arrow"
:class="{'fui-collapse__arrow-close':!isOpen,'fui-collapse__arrow-active': isOpen, 'fui-collapse__item-ani': animation}"
class="fui-collapse__arrow" :style="{marginRight:arrowRight+'rpx'}">
<view class="fui-collapse__arrow-inner" :style="{borderColor:arrowColor}"></view>
</view>
<view v-if="isBorder" :style="{background:borderColor,left:borderLeft+'rpx'}" class="fui-collapse__border"
:class="{'fui-collapse__border-color':!borderColor}"></view>
</view>
<view class="fui-collapse__content-wrap" :class="{'fui-collapse-__content-ani':animation}"
:style="{height: (isOpen?height:0) +'px',background:contentBg}">
<view :id="elId" ref="fui_collapse__el" class="fui-collapse__content"
:class="{'fui-collapse__content-open':isHeight}">
<slot name="content"></slot>
</view>
</view>
</view>
</template>
<script>
// #ifdef APP-NVUE
const dom = weex.requireModule('dom')
// #endif
export default {
name: 'fui-collapse-item',
emits: ['change'],
props: {
//item项索引或者唯一标识
index: {
type: [Number, String],
default: 0
},
// 是否禁用
disabled: {
type: Boolean,
default: false
},
background: {
type: String,
default: '#fff'
},
//是否显示动画,如果动画卡顿严重建议不开启
animation: {
type: Boolean,
default: true
},
// 是否展开
open: {
type: Boolean,
default: false
},
isBorder: {
type: Boolean,
default: true
},
// #ifdef APP-NVUE
borderColor: {
type: String,
default: '#EEEEEE'
},
// #endif
// #ifndef APP-NVUE
borderColor: {
type: String,
default: ''
},
// #endif
borderLeft: {
type: [Number, String],
default: 0
},
arrow: {
type: Boolean,
default: true
},
arrowColor: {
type: String,
default: '#B2B2B2'
},
arrowRight: {
type: [Number, String],
default: 24
},
contentBg: {
type: String,
default: '#fff'
},
marginTop: {
type: [Number, String],
default: 0
},
marginBottom: {
type: [Number, String],
default: 0
}
},
data() {
const elId = `fui_${Math.ceil(Math.random() * 10e5).toString(36)}`
return {
isOpen: false,
isHeight: null,
height: 0,
elId
}
},
watch: {
open(val) {
this.isOpen = val
}
},
updated(e) {
this.$nextTick(() => {
setTimeout(() => {
this.init()
}, 50)
})
},
created() {
this.collapse = this.getCollapse()
if (this.collapse && this.collapse.children.indexOf(this) === -1) {
this.collapse.children.push(this)
}
this.oldHeight = 0
},
// #ifndef VUE3
// TODO vue2
beforeDestroy() {
this.uninstall()
},
// #endif
// #ifdef VUE3
// TODO vue3
beforeUnmount() {
this.uninstall()
},
// #endif
mounted() {
this.$nextTick(() => {
setTimeout(() => {
this.init()
this.isOpen = this.open;
}, 50)
})
},
methods: {
init() {
// #ifndef APP-NVUE
this.getCollapseHeight()
// #endif
// #ifdef APP-NVUE
this.getNvueHeight()
// #endif
},
uninstall() {
if (this.collapse) {
this.collapse.children.forEach((item, index) => {
if (item === this) {
this.collapse.children.splice(index, 1)
}
})
}
},
onClick(isOpen) {
if (this.disabled) return
this.isOpen = isOpen
if (this.collapse) {
this.collapse.collapseChange(this, isOpen, this.index)
} else {
this.$emit('change', {
index: this.index,
isOpen: isOpen
})
}
},
getCollapseHeight(index = 0) {
uni.createSelectorQuery()
// #ifndef MP-ALIPAY
.in(this)
// #endif
.select(`#${this.elId}`)
.fields({
size: true
}, data => {
if (index >= 10) return
if (!data) {
index++
this.getCollapseHeight(index)
return
}
// #ifdef APP-NVUE
this.height = data.height + 1
// #endif
// #ifndef APP-NVUE
this.height = data.height
// #endif
this.isHeight = true
})
.exec()
},
getNvueHeight() {
const result = dom.getComponentRect(this.$refs['fui_collapse__el'], option => {
if (option && option.result && option.size) {
// #ifdef APP-NVUE
this.height = option.size.height + 1
// #endif
// #ifndef APP-NVUE
this.height = option.size.height
// #endif
this.isHeight = true
}
})
},
getCollapse(name = 'fui-collapse') {
let parent = this.$parent;
let parentName = parent.$options.name;
while (parentName !== name) {
parent = parent.$parent;
if (!parent) return false;
parentName = parent.$options.name;
}
return parent;
}
}
}
</script>
<style scoped>
.fui-collapse__item {
/* #ifndef APP-NVUE */
box-sizing: border-box;
/* #endif */
}
.fui-collapse-item__title {
/* #ifndef APP-NVUE */
display: flex;
width: 100%;
box-sizing: border-box;
/* #endif */
flex-direction: row;
align-items: center;
transition: border-bottom-color 0.3s;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
position: relative;
}
.fui-collapse__border {
position: absolute;
bottom: 0;
right: 0;
/* #ifdef APP-NVUE */
height: 0.5px;
z-index: -1;
/* #endif */
/* #ifndef APP-NVUE */
height: 1px;
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
transform-origin: 0 100%;
z-index: 1;
/* #endif */
}
/* #ifndef APP-NVUE */
.fui-collapse__border-color {
background: var(--fui-color-border, #EEEEEE) !important;
}
/* #endif */
.fui-collapse__disabled {
opacity: .5;
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
}
.fui-collapse__title {
flex: 1;
overflow: hidden;
}
.fui-collapse__arrow-inner {
height: 40rpx;
width: 40rpx;
border-width: 0 3px 3px 0;
border-style: solid;
transform: rotate(45deg) scale(.5);
/* #ifndef APP-NVUE */
box-sizing: border-box;
/* #endif */
position: absolute;
top: -6rpx;
left: 0;
}
.fui-collapse__arrow {
width: 40rpx;
height: 40rpx;
position: relative;
/* #ifndef APP-NVUE */
flex-shrink: 0;
/* #endif */
}
.fui-collapse__arrow-close {
transform: rotate(0deg);
}
.fui-collapse__arrow-active {
transform: rotate(180deg);
}
.fui-collapse__item-ani {
transition-property: transform;
transition-duration: 0.3s;
transition-timing-function: ease;
}
.fui-collapse__content-wrap {
/* #ifndef APP-NVUE */
will-change: height;
box-sizing: border-box;
/* #endif */
overflow: hidden;
position: relative;
height: 0;
}
.fui-collapse-__content-ani {
transition-property: height;
transition-duration: 0.3s;
/* #ifndef APP-NVUE */
will-change: height;
/* #endif */
}
.fui-collapse__content {
/* #ifndef APP-NVUE */
width: 100%;
/* #endif */
position: absolute;
}
.fui-collapse__content-open {
position: relative;
}
</style>
@@ -0,0 +1,53 @@
<template>
<!--本文件由FirstUI授权予佛山市航电梦联网络科技有限公司会员ID1 127营业执照号 9144 060 5M A5 56H 1K XH专用请尊重知识产权勿私下传播违者追究法律责任-->
<view class="fui-collapse__wrap" :style="{background:background}">
<slot></slot>
</view>
</template>
<script>
export default {
name: 'fui-collapse',
emits: ['change'],
props: {
// 是否开启手风琴效果
accordion: {
type: Boolean,
default: false
},
background: {
type: String,
default: 'transparent'
}
},
created() {
this.children = []
},
methods: {
collapseChange(obj, isOpen, idx) {
if (this.accordion && isOpen) {
this.children.forEach((item, index) => {
if (item !== obj) {
item.isOpen = false
}
})
}
this.$emit('change', {
index: idx,
isOpen: isOpen
})
}
}
}
</script>
<style scoped>
.fui-collapse__wrap {
/* #ifndef APP-NVUE */
width: 100%;
display: flex;
/* #endif */
/* #ifdef APP-NVUE */
flex: 1;
/* #endif */
flex-direction: column;
}
</style>
@@ -0,0 +1,72 @@
/*!
* common v1.0.0 (https://doc.firstui.cn)
* Copyright 2024 FirstUI.
* Licensed under the Apache license
*/
/* ipx 底部安全区域 */
.fs-safe__area{
/* #ifdef APP-NVUE || MP-TOUTIAO */
padding-bottom: 34px;
/* #endif */
/* #ifndef APP-NVUE || MP-TOUTIAO */
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
/* #endif */
}
/* hover */
.fs-hover,
.fs-text__hover{
/* #ifdef H5 */
cursor: pointer;
/* #endif */
}
.fs-hover:active{
background: $fv-bg-color-hover;
}
.fs-text__hover:active{
opacity: .5;
}
.fs-full{
/* #ifndef APP-NVUE */
width: 100%;
/* #endif */
/* #ifdef APP-NVUE */
width: 750rpx;
/* #endif */
}
.fs-disabled{
opacity: $fv-opacity-disabled;
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
}
/* thin 细边线 0.5px*/
.fs-cell__thin{
position: relative;
/* #ifdef APP-NVUE */
border-bottom: 0.5px solid $fv-color-border;
/* #endif */
}
/* #ifndef APP-NVUE */
.fs-cell__thin::after{
content: ' ';
position: absolute;
border-bottom: 1px solid $fv-color-border;
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
bottom: 0;
left: 32rpx;
right: 0;
z-index: 1;
pointer-events: none;
}
/* #endif */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,42 @@
/*!
* firstui style v1.0.0 (https://doc.firstui.cn)
* Copyright 2024 FirstUI.
* Licensed under the Apache license
*/
@import './variables/index.scss';
/* common */
@import './base/common.scss';
/* color */
@import './widget/color.scss';
/* font-size */
@import './widget/size.scss';
/* font-weight */
@import './widget/weight.scss';
/* align */
@import './widget/align.scss';
/* margin */
@import './widget/margin.scss';
/* padding */
@import './widget/padding.scss';
/* border */
@import './widget/border.scss';
/* border-radius */
@import './widget/radius.scss';
/* position */
@import './widget/position.scss';
/* flexbox */
@import './widget/flexbox.scss';
/* hide */
@import './widget/hide.scss';
@@ -0,0 +1,66 @@
/* fui-variables */
/* 行为相关颜色 */
$fv-color-primary: #465CFF !default;
$fv-color-success: #09BE4F !default;
$fv-color-warning: #FFB703 !default;
$fv-color-danger: #FF2B2B !default;
$fv-color-purple: #6831FF !default;
/* 文字基本颜色、其他辅助色 */
/* 用于重量级文字信息、标题 */
$fv-color-title: #181818 !default;
/* 用于普通级段落信息、引导词 */
$fv-color-section: #333333 !default;
/* 用于次要标题内容 */
$fv-color-subtitle: #7F7F7F !default;
/* 用于底部标签、描述、次要文字信息 */
$fv-color-label: #B2B2B2 !default;
/* 用于辅助、次要信息、禁用文字等。如:待输入状态描述文字,已点击按钮文字 */
$fv-color-minor: #CCCCCC !default;
$fv-color-white: #FFFFFF !default;
/* 链接颜色 */
$fv-color-link: #465CFF !default;
/* 背景颜色 */
$fv-bg-color: #ffffff !default;
/* 页面背景底色 */
$fv-bg-color-grey: #F1F4FA !default;
/* 内容模块底色 */
$fv-bg-color-content: #F8F8F8 !default;
/* 点击背景色 */
$fv-bg-color-hover: rgba(0, 0, 0, 0.2) !default;
/* 遮罩颜色 */
$fv-bg-color-mask: rgba(0, 0, 0, 0.6) !default;
/* 边框颜色 */
$fv-color-border: #EEEEEE !default;
/* 阴影颜色 */
$fv-color-shadow: rgba(2, 4, 38, 0.05) !default;
/*禁用态的透明度 */
$fv-opacity-disabled: 0.5 !default;
/* icon尺寸 */
$fv-icon-size: 64rpx !default;
/* Border Radius */
$fv-border-radius-sm: 16rpx !default;
$fv-border-radius-base: 24rpx !default;
$fv-border-radius-lg: 48rpx !default;
/* 水平间距 */
$fv-spacing-row-sm: 16rpx !default;
$fv-spacing-row-base: 24rpx !default;
$fv-spacing-row-lg: 32rpx !default;
/* 垂直间距 */
$fv-spacing-col-sm: 8rpx !default;
$fv-spacing-col-base: 16rpx !default;
$fv-spacing-col-lg: 24rpx !default;
/* 边框宽度 */
$fv-border-width:1px !default;
@@ -0,0 +1,31 @@
/*!
* text-align v1.0.0 (https://doc.firstui.cn)
* Copyright 2024 FirstUI.
* Licensed under the Apache license
*/
.fs-align__left{
text-align: left;
}
.fs-align__center{
text-align: center;
}
.fs-align__right{
text-align: right;
}
/* #ifndef APP-NVUE */
.fs-align__justify{
text-align: justify;
}
.fs-align__start{
text-align: start;
}
.fs-align__end{
text-align: end;
}
/* #endif */
@@ -0,0 +1,32 @@
/*!
* border v1.0.0 (https://doc.firstui.cn)
* Copyright 2024 FirstUI.
* Licensed under the Apache license
*/
.fs-border {
border-style: solid;
border-width: $fv-border-width;
}
.fs-border__none { border-width: 0 }
.fs-border__top {
border-top-style: solid;
border-top-width: $fv-border-width;
}
.fs-border__right {
border-right-style: solid;
border-right-width: $fv-border-width;
}
.fs-border__bottom {
border-bottom-style: solid;
border-bottom-width: $fv-border-width;
}
.fs-border__left {
border-left-style: solid;
border-left-width: $fv-border-width;
}
@@ -0,0 +1,88 @@
/*!
* color v1.0.0 (https://doc.firstui.cn)
* Copyright 2024 FirstUI.
* Licensed under the Apache license
*/
/* color */
.fs-color__primary {
color: $fv-color-primary;
}
.fs-color__success {
color: $fv-color-success;
}
.fs-color__warning {
color: $fv-color-warning;
}
.fs-color__danger {
color: $fv-color-danger;
}
.fs-color__purple {
color: $fv-color-purple;
}
/* 用于重量级文字信息、标题 */
.fs-color__title {
color: $fv-color-title;
}
/* 用于普通级段落信息、引导词 */
.fs-color__section{
color: $fv-color-section;
}
/* 用于次要标题内容 */
.fs-color__subtitle{
color: $fv-color-subtitle
}
/* 用于底部标签、描述、次要文字信息 */
.fs-color__label{
color: $fv-color-label;
}
/* 用于辅助、次要信息、禁用文字等。如:待输入状态描述文字,已点击按钮文字 */
.fs-color__minor{
color: $fv-color-minor;
}
.fs-color__white{
color: $fv-color-white;
}
/* 链接颜色 */
.fs-color__link{
color: $fv-color-link;
}
/* bgckground-color */
.fs-bg__primary {
background: $fv-color-primary;
}
.fs-bg__success {
background: $fv-color-success;
}
.fs-bg__warning {
background: $fv-color-warning;
}
.fs-bg__danger {
background: $fv-color-danger;
}
.fs-bg__purple {
background: $fv-color-purple;
}
.fs-bg__white{
background: $fv-color-white;
}
/* 页面背景底色 */
.fs-bg__page{
background:$fv-bg-color-grey;
}
/* 内容模块底色 */
.fs-bg__content{
background:$fv-bg-color-content;
}
/* 点击背景颜色 */
.fs-bg__hover{
background:$fv-bg-color-hover;
}
/* 遮罩背景颜色 */
.fs-bg__mask{
background:$fv-bg-color-mask;
}
@@ -0,0 +1,99 @@
/*!
* flexbox v1.0.0 (https://doc.firstui.cn)
* Copyright 2024 FirstUI.
* Licensed under the Apache license
*/
.fs-flex,
.fs-flex__row {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.fs-flex__column {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
}
.fs-flex__wrap { flex-wrap: wrap }
.fs-items__start { align-items: flex-start }
.fs-items__end { align-items: flex-end }
.fs-items__center { align-items: center }
/* #ifndef APP-NVUE */
.fs-items__baseline { align-items: baseline }
/* #endif */
.fs-items__stretch { align-items: stretch }
/* #ifndef APP-NVUE */
.fs-self__start { align-self: flex-start }
.fs-self__end { align-self: flex-end }
.fs-self__center { align-self: center }
.fs-self__baseline { align-self: baseline }
.fs-self__stretch { align-self: stretch }
/* #endif */
.fs-justify__start { justify-content: flex-start }
.fs-justify__end { justify-content: flex-end }
.fs-justify__center { justify-content: center }
.fs-justify__between { justify-content: space-between }
.fs-justify__around { justify-content: space-around }
/* #ifndef APP-NVUE */
.fs-justify__evenly { justify-content: space-evenly }
/* #endif */
/* #ifndef APP-NVUE */
.fs-content__start { align-content: flex-start }
.fs-content__end { align-content: flex-end }
.fs-content__center { align-content: center }
.fs-content__between { align-content: space-between }
.fs-content__around { align-content: space-around }
.fs-content__stretch { align-content: stretch }
/* #endif */
/* #ifndef APP-NVUE */
.fs-order__0 { order: 0 }
.fs-order__1 { order: 1 }
.fs-order__2 { order: 2 }
.fs-order__3 { order: 3 }
.fs-order__last { order: 99999 }
/* #endif */
.fs-flex__center{
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
}
.fs-flex__between {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: space-between;
}
.fs-align__center {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
align-items: center;
flex-direction: row;
}
.fs-flex__reverse {
flex-direction: row-reverse;
}
.fs-flex__1,
.fs-flex1{
flex: 1;
}
@@ -0,0 +1,46 @@
/*!
* hide v1.0.0 (https://doc.firstui.cn)
* Copyright 2024 FirstUI.
* Licensed under the Apache license
*/
.fs-hide{
position: absolute;
left: -6666px;
top: -6666px;
opacity: 0;
/* #ifndef APP-NVUE */
visibility: hidden;
z-index: -1;
/* #endif */
overflow: hidden;
}
/* #ifndef APP-NVUE */
.fs-display__none {
display: none !important
}
/* #endif */
.fs-ellipsis{
/* #ifndef APP-NVUE */
white-space: nowrap;
/* #endif */
overflow: hidden;
text-overflow: ellipsis;
/* #ifdef APP-NVUE */
lines:1;
/* #endif */
}
.fs-ellipsis__2{
/* #ifndef APP-NVUE */
word-break: break-all;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
/* #endif */
overflow: hidden;
text-overflow: ellipsis;
/* #ifdef APP-NVUE */
lines:2;
/* #endif */
}
@@ -0,0 +1,21 @@
/*!
* margin v1.0.0 (https://doc.firstui.cn)
* Copyright 2024 FirstUI.
* Licensed under the Apache license
*/
$fv-margin:0,2,4,8,10,12,16,20,24,28,30,32,36,40,44,48,52,56,60,64,68,72,76,80,84,88,92,96;
@each $m in $fv-margin{
.fs-m#{$m} { margin: #{$m}rpx;}
.fs-mt#{$m} { margin-top: #{$m}rpx;}
.fs-mr#{$m} { margin-right: #{$m}rpx; }
.fs-mb#{$m} { margin-bottom: #{$m}rpx; }
.fs-ml#{$m} { margin-left: #{$m}rpx; }
.fs-mx#{$m} { margin-left: #{$m}rpx; margin-right: #{$m}rpx; }
.fs-my#{$m} { margin-top:#{$m}rpx; margin-bottom: #{$m}rpx; }
}
/* #ifndef APP-NVUE */
.fs-ml__auto { margin-left: auto; }
.fs-mr__auto { margin-right: auto; }
/* #endif */
@@ -0,0 +1,17 @@
/*!
* padding v1.0.0 (https://doc.firstui.cn)
* Copyright 2024 FirstUI.
* Licensed under the Apache license
*/
$fv-padding:0,2,4,8,10,12,16,20,24,28,30,32,36,40,44,48,52,56,60,64,68,72,76,80,84,88,92,96,120,160,200;
@each $p in $fv-padding{
.fs-p#{$p} { padding: #{$p}rpx;}
.fs-pt#{$p} { padding-top: #{$p}rpx;}
.fs-pr#{$p} { padding-right: #{$p}rpx; }
.fs-pb#{$p} { padding-bottom: #{$p}rpx; }
.fs-pl#{$p} { padding-left: #{$p}rpx; }
.fs-px#{$p} { padding-left: #{$p}rpx; padding-right: #{$p}rpx; }
.fs-py#{$p} { padding-top:#{$p}rpx; padding-bottom: #{$p}rpx; }
}
@@ -0,0 +1,20 @@
/*!
* position v1.0.0 (https://doc.firstui.cn)
* Copyright 2024 FirstUI.
* Licensed under the Apache license
*/
.fs-relative { position: relative }
.fs-absolute { position: absolute }
.fs-fixed { position: fixed }
.fs-sticky { position: sticky }
.fs-top0 { top: 0 }
.fs-right0 { right: 0 }
.fs-bottom0 { bottom: 0 }
.fs-left0 { left: 0 }
/* z-index */
@for $i from 1 through 9 {
.fs-z#{$i} { z-index: #{$i} }
}
@@ -0,0 +1,36 @@
/*!
* border-radius v1.0.0 (https://doc.firstui.cn)
* Copyright 2024 FirstUI.
* Licensed under the Apache license
*/
$fv_radius:4,8,12,16,24,30,48,200;
@each $r in $fv_radius{
.fs-radius__#{$r} {
border-radius: #{$r}rpx;
overflow: hidden;
}
}
.fs-radius__sm{
border-radius: $fv-border-radius-sm;
overflow: hidden;
}
.fs-radius__base,
.fs-radius__md{
border-radius: $fv-border-radius-base;
overflow: hidden;
}
.fs-radius__lg{
border-radius: $fv-border-radius-lg;
overflow: hidden;
}
.fs-radius__circle{
/* #ifndef APP-NVUE */
border-radius: 50%;
/* #endif */
/* #ifdef APP-NVUE */
border-radius: 200px;
/* #endif */
overflow: hidden;
}
@@ -0,0 +1,39 @@
/*!
* font-size v1.0.0 (https://doc.firstui.cn)
* Copyright 2024 FirstUI.
* Licensed under the Apache license
*/
/* 常用字体大小 单位rpx*/
.fs-size__h1 {
font-size: 44rpx;
font-weight: 500;
}
.fs-size__h2 {
font-size: 36rpx;
font-weight: 500;
}
.fs-size__h3 {
font-size: 32rpx;
font-weight: 400;
}
.fs-size__h4{
font-size: 28rpx;
font-weight: 400;
}
.fs-size__h5,
.fs-size__h6 {
font-size: 24rpx;
font-weight: 400;
}
/* 自定义字体大小 24~64 单位rpx*/
@for $i from 24 through 64 {
.fs-size__#{$i}{
font-size: #{$i}rpx;
}
}
@@ -0,0 +1,24 @@
/*!
* font-weight v1.0.0 (https://doc.firstui.cn)
* Copyright 2024 FirstUI.
* Licensed under the Apache license
*/
.fs-weight__400,
.fs-weight__normal{
font-weight: 400;
}
.fs-weight__500{
font-weight: 500;
}
.fs-weight__600{
font-weight: 600;
}
.fs-bold,
.fs-weight__bold,
.fs-weight__700,
.fs-weight__800,
.fs-weight__900{
font-weight: bold;
}
@@ -0,0 +1,131 @@
<template>
<!--本文件由FirstUI授权予佛山市航电梦联网络科技有限公司会员ID1 1 27营业执照号 9 1 440 60 5MA 55 6 H 1 KX H专用请尊重知识产权勿私下传播违者追究法律责任-->
<view class="fui-empty__wrap" :class="{'fui-empty__fixed':isFixed}" :style="{marginTop:marginTop+'rpx'}">
<image :src="src" :style="{width:width+'rpx',height:height+'rpx'}" mode="widthFix" v-if="src"></image>
<text class="fui-empty__title" :class="{'fui-empty__title-color':!color}"
:style="{color:color,fontSize:size+'rpx'}" v-if="title">{{title}}</text>
<text class="fui-empty__desc" :class="{'fui-empty__descr-color':!descrColor}"
:style="{color:descrColor,fontSize:descrSize+'rpx'}" v-if="descr">{{descr}}</text>
<slot></slot>
</view>
</template>
<script>
export default {
name: "fui-empty",
props: {
src: {
type: String,
default: ''
},
width: {
type: [Number, String],
default: 576
},
height: {
type: [Number, String],
default: 318
},
title: {
type: String,
default: ''
},
// #ifdef APP-NVUE
color: {
type: String,
default: '#333333'
},
// #endif
// #ifndef APP-NVUE
color: {
type: String,
default: ''
},
// #endif
size: {
type: [Number, String],
default: 32
},
descr: {
type: String,
default: ''
},
// #ifdef APP-NVUE
descrColor: {
type: String,
default: '#B2B2B2'
},
// #endif
// #ifndef APP-NVUE
descrColor: {
type: String,
default: ''
},
// #endif
descrSize: {
type: [Number, String],
default: 24
},
isFixed: {
type: Boolean,
default: false
},
marginTop: {
type: [Number, String],
default: 0
}
}
}
</script>
<style scoped>
.fui-empty__wrap {
flex: 1;
/* #ifndef APP-NVUE */
width: 100%;
display: flex;
/* #endif */
flex-direction: column;
align-items: center;
justify-content: center;
}
.fui-empty__fixed {
position: fixed;
left: 0;
/* #ifndef APP-NVUE */
top: 50%;
transform: translateY(-50%);
/* #endif */
/* #ifdef APP-NVUE */
top: 0;
right: 0;
bottom: 0;
/* #endif */
z-index: 99;
}
.fui-empty__title {
text-align: center;
font-weight: 500;
padding-top: 48rpx;
}
.fui-empty__desc {
text-align: center;
font-weight: normal;
padding-top: 8rpx;
}
/* #ifndef APP-NVUE */
.fui-empty__title-color {
color: var(--fui-color-section, #333333) !important;
}
.fui-empty__descr-color {
color: var(--fui-color-label, #B2B2B2) !important;
}
/* #endif */
</style>
@@ -0,0 +1,83 @@
// 本文件由FirstUI授权予佛山市航电梦联网络科技有限公司(会员ID: 11 27,营业执照号:9 1440605MA 556 H 1 K X H)专用,请尊重知识产权,勿私下传播,违者追究法律责任。
// #ifdef APP-NVUE
const animation = uni.requireNativePlugin('animation');
export default {
data() {
return {
startX: 0,
startY: 0,
lastLeft: 0,
lastTop: 0,
isMove: false
}
},
created() {
this.refFab = null;
this.loop = null
},
mounted() {
this.$nextTick(() => {
setTimeout(() => {
this.refFab = this.getEl(this.$refs['fui_fab_move_ref'])
}, 50)
})
},
methods: {
getEl(el) {
return el.ref || el[0].ref;
},
_aniMove(x, y) {
if (!this.refFab || !this.isDrag) return
animation.transition(this.refFab, {
styles: {
transform: `translate(${x}px,${y}px)`
},
duration: 0,
timingFunction: 'linear',
needLayout: false,
delay: 0
}, () => {
if (Math.abs(x) > 0.1 || Math.abs(y) > 0.1) {
this.isMove = true;
}
});
},
touchstart(e) {
if (!this.isDrag) return;
var touch = e.touches || e.changedTouches
this.startX = touch[0].screenX
this.startY = touch[0].screenY
},
touchmove(e) {
if (!this.isDrag) return;
var touch = e.touches || e.changedTouches
let pageX = touch[0].screenX,
pageY = touch[0].screenY;
var left = pageX - this.startX + this.lastLeft;
left = left < -this.eLeft ? -this.eLeft : left;
left = left > this.maxWidth ? this.maxWidth : left;
this.startX = pageX
var top = pageY - this.startY + this.lastTop;
top = top < -this.eTop ? -this.eTop : top;
top = top > this.maxHeight ? this.maxHeight : top;
this.startY = pageY
this.lastLeft = left
this.lastTop = top
this._aniMove(left, top)
},
touchend(e) {
clearTimeout(this.loop)
this.loop = setTimeout(() => {
this.isMove = false
}, 50)
}
}
}
// #endif
// #ifndef APP-NVUE
export default {}
// #endif
+708
View File
@@ -0,0 +1,708 @@
<template>
<!--本文件由FirstUI授权予佛山市航电梦联网络科技有限公司会员ID 1 127营业执照号9 1 44060 5M A 556 H 1 K XH专用请尊重知识产权勿私下传播违者追究法律责任-->
<view @touchmove.stop.prevent="stop">
<view class="fui-fab__mask" :class="{'fui-fab__mask-show':isShow}" :style="getStyle" ref="fui_mask_ani"
v-if="mask" @tap.stop="maskClick"></view>
<!-- #ifdef APP-VUE || MP-WEIXIN || H5-->
<view class="fui-fab__btn-wrap" :data-disabled="isDrag?0:1" :data-width="maxWidth" :data-height="maxHeight"
:data-left="eLeft" :data-top="eTop" :data-app="isApp" :prop="resetNum" :change:prop="handler.fabreset"
@touchstart="handler.touchstart" @touchmove="handler.touchmove" @mousedown="handler.mousedown"
:class="[position==='left'?'fui-fab__wrap-left':'fui-fab__wrap-right']" :style="getStyles">
<view class="fui-fab__btn-list"
:class="{'fui-fab__btn-hidden':isHidden,'fui-fab__list-ani':isShow,'fui-fab__list-left':position==='left','fui-fab__list-right':position==='right'}"
ref="fui_fab_ani">
<view class="fui-fab__button-box"
:class="[position==='left'?'fui-fab__button-left':'fui-fab__button-right']"
v-for="(btn,idx) in fabs" :key="idx" @tap.stop="handleClick($event,idx)">
<text class="fui-fab__btn-text" v-if="btn[textKey]"
:style="{fontSize:(btn.size || 32)+'rpx',color:btn.color || '#fff',textAlign:position==='left'?'left':'right'}">{{btn[textKey]}}</text>
<view class="fui-fab__button" :class="{'fui-fab__btn-color':!getBgColor && !btn.background }"
:style="{width:width+'rpx',height:width+'rpx',background:btn.background || getBgColor}">
<fui-icon :name="btn[nameKey]" v-if="btn[nameKey]" :color="btn.abbrColor || '#fff'"
:size="btn.abbrSize || 64"></fui-icon>
<image :src="btn[srcKey]"
:style="{width:(btn.width || 56)+'rpx',height:(btn.height || 56)+'rpx',borderRadius:(btn.radius || 0)+'rpx'}"
v-if="!btn[nameKey] && btn[srcKey]" mode="widthFix"></image>
<text class="fui-fab__btn-abbr"
:style="{fontSize:(btn.abbrSize || 36)+'rpx',lineHeight:(btn.abbrSize || 36)+'rpx',color:btn.abbrColor || '#fff'}"
v-if="!btn[nameKey] && !btn[srcKey] && btn.abbr">{{btn.abbr}}</text>
</view>
<!-- #ifndef H5 -->
<button class="fui-fab__opentype-btn" :open-type="btn.openType" :app-parameter="btn.appParameter"
:lang="btn.lang" :sessionFrom="btn.sessionFrom" :sendMessageTitle="btn.sendMessageTitle"
:sendMessagePath="btn.sendMessagePath" :sendMessageImg="btn.sendMessageImg"
:showMessageCard="btn.showMessageCard" @contact="bindcontact" @opensetting="bindopensetting"
@launchapp="bindlaunchapp" v-if="btn.openType"></button>
<!-- #endif -->
</view>
</view>
<view class="fui-fab__btn-main" :class="{'fui-fab__btn-color':!getBgColor}"
:style="{width:width+'rpx',height:width+'rpx',background:getBgColor}"
@tap.stop="handleClick($event,-1)">
<view class="fui-fab__btn-inner" :class="{'fui-fab__btn-ani':isShow}" ref="fui_fm_ani">
<slot>
<fui-icon name="plus" :color="color" :size="80"></fui-icon>
</slot>
</view>
<!-- #ifndef H5 -->
<button class="fui-fab__opentype-btn" :open-type="openType" :app-parameter="appParameter" :lang="lang"
:sessionFrom="sessionFrom" :sendMessageTitle="sendMessageTitle" :sendMessagePath="sendMessagePath"
:sendMessageImg="sendMessageImg" :showMessageCard="showMessageCard" @contact="bindcontact"
@opensetting="bindopensetting" @launchapp="bindlaunchapp" v-if="openType"></button>
<!-- #endif -->
</view>
</view>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<view class="fui-fab__btn-wrap" @touchstart="touchstart" @touchmove.stop.prevent="touchmove"
@touchend="touchend" @touchcancel="touchend" ref="fui_fab_move_ref"
:class="[position==='left'?'fui-fab__wrap-left':'fui-fab__wrap-right']" :style="getStyles">
<view class="fui-fab__btn-list"
:class="{'fui-fab__btn-hidden':isHidden,'fui-fab__list-ani':isShow,'fui-fab__list-left':position==='left','fui-fab__list-right':position==='right'}"
ref="fui_fab_ani">
<view class="fui-fab__button-box"
:class="[position==='left'?'fui-fab__button-left':'fui-fab__button-right']"
v-for="(btn,idx) in fabs" :key="idx" @tap.stop="handleClick($event,idx)">
<text class="fui-fab__btn-text" v-if="btn[textKey]"
:style="{fontSize:(btn.size || 32)+'rpx',color:btn.color || '#fff',textAlign:position==='left'?'left':'right'}">{{btn[textKey]}}</text>
<view class="fui-fab__button" :class="{'fui-fab__btn-color':!getBgColor && !btn.background }"
:style="{width:width+'rpx',height:width+'rpx',background:btn.background || getBgColor}">
<fui-icon :name="btn[nameKey]" v-if="btn[nameKey]" :color="btn.abbrColor || '#fff'"
:size="btn.abbrSize || 64"></fui-icon>
<image :src="btn[srcKey]"
:style="{width:(btn.width || 56)+'rpx',height:(btn.height || 56)+'rpx',borderRadius:(btn.radius || 0)+'rpx'}"
v-if="!btn[nameKey] && btn[srcKey]" mode="widthFix"></image>
<text class="fui-fab__btn-abbr"
:style="{fontSize:(btn.abbrSize || 36)+'rpx',lineHeight:(btn.abbrSize || 36)+'rpx',color:btn.abbrColor || '#fff'}"
v-if="!btn[nameKey] && !btn[srcKey] && btn.abbr">{{btn.abbr}}</text>
</view>
</view>
</view>
<view class="fui-fab__btn-main" :class="{'fui-fab__btn-color':!getBgColor}"
:style="{width:width+'rpx',height:width+'rpx',background:getBgColor}"
@tap.stop="handleClick($event,-1)">
<view class="fui-fab__btn-inner" :class="{'fui-fab__btn-ani':isShow}" ref="fui_fm_ani">
<slot>
<fui-icon name="plus" :color="color" :size="80"></fui-icon>
</slot>
</view>
</view>
</view>
<!-- #endif -->
<!-- #ifndef APP-PLUS || MP-WEIXIN || H5 -->
<view class="fui-fab__btn-wrap" @touchstart="touchstart" @touchmove.stop.prevent="touchmove"
:class="[position==='left'?'fui-fab__wrap-left':'fui-fab__wrap-right']" :style="getStyles">
<view class="fui-fab__btn-list"
:class="{'fui-fab__btn-hidden':isHidden,'fui-fab__list-ani':isShow,'fui-fab__list-left':position==='left','fui-fab__list-right':position==='right'}"
ref="fui_fab_ani">
<view class="fui-fab__button-box"
:class="[position==='left'?'fui-fab__button-left':'fui-fab__button-right']"
v-for="(btn,idx) in fabs" :key="idx" @tap.stop="handleClick($event,idx)">
<text class="fui-fab__btn-text" v-if="btn[textKey]"
:style="{fontSize:(btn.size || 32)+'rpx',color:btn.color || '#fff',textAlign:position==='left'?'left':'right'}">{{btn[textKey]}}</text>
<view class="fui-fab__button" :class="{'fui-fab__btn-color':!getBgColor && !btn.background }"
:style="{width:width+'rpx',height:width+'rpx',background:btn.background || getBgColor}">
<fui-icon :name="btn[nameKey]" v-if="btn[nameKey]" :color="btn.abbrColor || '#fff'"
:size="btn.abbrSize || 64"></fui-icon>
<image :src="btn[srcKey]"
:style="{width:(btn.width || 56)+'rpx',height:(btn.height || 56)+'rpx',borderRadius:(btn.radius || 0)+'rpx'}"
v-if="!btn[nameKey] && btn[srcKey]" mode="widthFix"></image>
<text class="fui-fab__btn-abbr"
:style="{fontSize:(btn.abbrSize || 36)+'rpx',lineHeight:(btn.abbrSize || 36)+'rpx',color:btn.abbrColor || '#fff'}"
v-if="!btn[nameKey] && !btn[srcKey] && btn.abbr">{{btn.abbr}}</text>
</view>
</view>
</view>
<view class="fui-fab__btn-main" :class="{'fui-fab__btn-color':!getBgColor}"
:style="{width:width+'rpx',height:width+'rpx',background:getBgColor}"
@tap.stop="handleClick($event,-1)">
<view class="fui-fab__btn-inner" :class="{'fui-fab__btn-ani':isShow}" ref="fui_fm_ani">
<slot>
<fui-icon name="plus" :color="color" :size="80"></fui-icon>
</slot>
</view>
</view>
</view>
<!-- #endif -->
</view>
</template>
<!-- #ifdef APP-VUE || MP-WEIXIN || H5-->
<script src="./index.wxs" module="handler" lang="wxs"></script>
<!-- #endif -->
<script>
//非easycom模式取消注释引入字体组件,按实际路径进行调整
// import fuiIcon from "@/components/firstui/fui-icon/fui-icon.vue"
// #ifdef APP-NVUE
const animation = uni.requireNativePlugin('animation');
const dom = uni.requireNativePlugin('dom');
// #endif
import mpjs from './mpjs.js'
import bindingx from './bindingx.js'
export default {
name: "fui-fab",
mixins: [mpjs, bindingx],
emits: ['click', 'change', 'opensetting', 'launchapp', 'contact'],
// components:{
// fuiIcon
// },
props: {
fabs: {
type: Array,
default () {
return []
}
},
nameKey: {
type: String,
default: 'name'
},
srcKey: {
type: String,
default: 'src'
},
textKey: {
type: String,
default: 'text'
},
position: {
type: String,
default: 'right'
},
distance: {
type: [Number, String],
default: 80
},
bottom: {
type: [Number, String],
default: 120
},
width: {
type: [Number, String],
default: 108
},
background: {
type: String,
default: ""
},
color: {
type: String,
default: "#fff"
},
mask: {
type: Boolean,
default: true
},
maskBackground: {
type: String,
default: 'rgba(0,0,0,.6)'
},
maskClosable: {
type: Boolean,
default: false
},
zIndex: {
type: [Number, String],
default: 99
},
//V1.9.8+
isDrag: {
type: Boolean,
default: false
},
//v2.3.0+
openType: {
type: String,
default: ''
},
appParameter: {
type: String,
default: ''
},
lang: {
type: String,
default: 'en'
},
sessionFrom: {
type: String,
default: ''
},
sendMessageTitle: {
type: String,
default: ''
},
sendMessagePath: {
type: String,
default: ''
},
sendMessageImg: {
type: String,
default: ''
},
showMessageCard: {
type: Boolean,
default: false
}
},
computed: {
getStyles() {
let styles = `bottom:${this.bottom}rpx;z-index:${this.zIndex};`
if (this.position === 'left') {
styles += `left:${this.distance}rpx;`
} else {
styles += `right:${this.distance}rpx;`
}
// #ifndef APP-PLUS || MP-WEIXIN || H5
if (this.isDrag) {
styles += `transform:${this.transform};`
}
// #endif
return styles;
},
getStyle() {
return `background:${this.maskBackground};z-index:${Number(this.zIndex)-10};`
},
getBgColor() {
let color = this.background;
// #ifdef APP-NVUE
if (!color || color === true) {
const app = uni && uni.$fui && uni.$fui.color;
color = (app && app.primary) || '#465CFF';
}
// #endif
return color;
}
},
watch: {
isShow(val) {
this.$emit("change", {
isShow: val
})
},
position(val) {
this.resetNum++;
this.$nextTick(() => {
setTimeout(() => {
this._getSize()
}, 80);
})
}
},
data() {
let isApp = 0;
// #ifdef APP
isApp = 1;
// #endif
return {
isApp,
isShow: false,
isHidden: true,
timer: null,
maxWidth: 0,
maxHeight: 0,
eLeft: 0,
eTop: 0,
resetNum: 0
};
},
// #ifndef APP-NVUE
// #ifndef VUE3
beforeDestroy() {
clearTimeout(this.timer)
this.timer = null
},
// #endif
// #ifdef VUE3
beforeUnmount() {
clearTimeout(this.timer)
this.timer = null
},
// #endif
// #endif
mounted() {
this.$nextTick(() => {
// #ifdef APP-NVUE
if (!this.$refs['fui_fab_ani']) return;
let styles = {
transform: 'scale(0)',
opacity: 0
}
animation.transition(
this.$refs['fui_fab_ani'].ref, {
styles,
duration: 0,
needLayout: false,
delay: 0
},
() => {}
);
// #endif
setTimeout(() => {
this._getSize()
}, 50);
})
},
methods: {
stop() {},
_getSize() {
if (!this.isDrag) return;
const sys = uni.getSystemInfoSync()
// #ifndef APP-NVUE
uni.createSelectorQuery()
// #ifndef MP-ALIPAY
.in(this)
// #endif
.select('.fui-fab__btn-wrap')
.boundingClientRect()
.exec(ret => {
if (ret) {
this.maxWidth = sys.windowWidth - ret[0].width - ret[0].left;
this.maxHeight = sys.windowHeight - ret[0].height - ret[0].top;
this.eLeft = ret[0].left || 0;
this.eTop = ret[0].top || 0;
}
})
// #endif
// #ifdef APP-NVUE
dom.getComponentRect(this.$refs['fui_fab_move_ref'], (ret) => {
const size = ret.size
if (size) {
this.maxWidth = sys.windowWidth - size.width - size.left;
this.maxHeight = sys.windowHeight - size.height - size.top;
this.eLeft = size.left || 0;
this.eTop = size.top || 0;
}
})
// #endif
},
// #ifdef APP-NVUE
_animation(type) {
let styles = {
transform: `scale(${type ? 1 : 0})`,
opacity: type ? 1 : 0
}
if (!this.$refs['fui_fab_ani'] || !this.$refs['fui_fm_ani']) return;
if (this.mask && this.$refs['fui_mask_ani']) {
animation.transition(
this.$refs['fui_mask_ani'].ref, {
styles: {
transform: `translateX(${type ? '0' : '-100%'})`
},
duration: 0,
needLayout: false,
delay: 0 //ms
},
() => {}
);
}
animation.transition(
this.$refs['fui_fm_ani'].ref, {
styles: {
transform: `rotate(${type ? '135deg' : '0deg'})`
},
duration: 250,
timingFunction: 'ease-in-out',
needLayout: false,
delay: 0 //ms
},
() => {
if (!type) {
this.isHidden = true
}
}
);
animation.transition(
this.$refs['fui_fab_ani'].ref, {
styles,
duration: 250,
timingFunction: 'ease-in-out',
needLayout: false,
delay: 0 //ms
},
() => {}
);
},
// #endif
handleClick: function(e, index) {
// #ifdef APP-NVUE
e.stopPropagation();
// #endif
// #ifdef APP-NVUE
if (this.isMove) {
this.isMove = false;
return;
};
// #endif
this.isHidden = false
clearTimeout(this.timer)
this.$nextTick(() => {
if (index === -1 && this.fabs.length > 0) {
this.isShow = !this.isShow
// #ifdef APP-NVUE
this._animation(this.isShow)
// #endif
} else {
this.$emit("click", {
index: index
})
this.isShow = false
// #ifdef APP-NVUE
this._animation(this.isShow)
// #endif
}
// #ifndef APP-NVUE
if (!this.isShow) {
this.timer = setTimeout(() => {
this.isHidden = true
}, 250)
}
// #endif
})
},
maskClick(e) {
// #ifdef APP-NVUE
e.stopPropagation();
// #endif
if (!this.maskClosable) return;
this.isShow = false
// #ifndef APP-NVUE
this.timer = setTimeout(() => {
this.isHidden = true
}, 250)
// #endif
// #ifdef APP-NVUE
this._animation(this.isShow)
// #endif
},
bindopensetting({
detail = {}
} = {}) {
this.$emit('opensetting', detail);
},
bindlaunchapp({
detail = {}
} = {}) {
this.$emit('launchapp', detail);
},
bindcontact({
detail = {}
} = {}) {
this.$emit('contact', detail);
}
}
}
</script>
<style scoped>
.fui-fab__mask {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
opacity: 0;
/* #ifndef APP-NVUE */
visibility: hidden;
transition-property: visibility, opacity;
/* #endif */
transition-duration: .25s;
/* #ifdef APP-NVUE */
transition-property: opacity;
transition-timing-function: ease-in-out;
transform: translateX(-100%);
/* #endif */
}
.fui-fab__mask-show {
opacity: 1;
/* #ifdef APP-NVUE */
transform: translateX(0);
/* #endif */
/* #ifndef APP-NVUE */
visibility: visible;
/* #endif */
}
.fui-fab__btn-wrap {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
position: fixed;
bottom: 120rpx;
}
.fui-fab__wrap-left {
align-items: flex-start;
left: 80rpx;
}
.fui-fab__wrap-right {
align-items: flex-end;
right: 80rpx;
}
.fui-fab__btn-list {
/* #ifndef APP-NVUE */
display: flex;
visibility: hidden;
transition: all 0.25s ease-in-out;
transform: scale3d(0, 0, 1);
opacity: 0;
/* #endif */
flex-direction: column;
}
.fui-fab__list-left {
transform-origin: 0 100%;
align-items: flex-start;
}
.fui-fab__list-right {
transform-origin: 100% 100%;
align-items: flex-end;
}
.fui-fab__btn-hidden {
width: 0;
height: 0;
}
.fui-fab__list-ani {
/* #ifndef APP-NVUE */
opacity: 1;
transform: scale3d(1, 1, 1);
visibility: visible;
/* #endif */
}
.fui-fab__button-box {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: flex-end;
align-items: center;
margin-bottom: 32rpx;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
position: relative;
}
.fui-fab__button-left {
flex-direction: row-reverse;
justify-content: flex-start;
}
.fui-fab__button-right {
flex-direction: row;
justify-content: flex-end;
}
.fui-fab__btn-text {
/* #ifndef APP-NVUE */
box-sizing: border-box;
/* #endif */
padding-left: 24rpx;
padding-right: 24rpx;
font-weight: normal;
}
.fui-fab__button {
/* #ifndef APP-NVUE */
display: flex;
border-radius: 50%;
/* #endif */
/* #ifdef APP-NVUE */
border-radius: 100px;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
}
.fui-fab__btn-abbr {
text-align: center;
font-weight: normal;
}
.fui-fab__btn-main {
/* #ifndef APP-NVUE */
display: flex;
border-radius: 50%;
/* #endif */
/* #ifdef APP-NVUE */
border-radius: 100px;
/* #endif */
/* #ifndef APP-NVUE */
box-shadow: 0 10rpx 14rpx 0 rgba(0, 0, 0, 0.1);
/* #endif */
align-items: center;
justify-content: center;
transform: rotate(0deg);
overflow: hidden;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
position: relative;
}
.fui-fab__btn-inner {
/* #ifndef APP-NVUE */
display: flex;
transform: rotate(0deg);
transition: transform .25s;
/* #endif */
align-items: center;
justify-content: center;
}
/* #ifndef APP-NVUE */
.fui-fab__btn-ani {
transform: rotate(135deg);
}
/* #endif */
/* #ifndef APP-NVUE */
.fui-fab__btn-color {
background: var(--fui-color-primary, #465CFF) !important;
}
/* #endif */
.fui-fab__opentype-btn {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
border-width: 0;
background-color: rgba(0, 0, 0, 0);
opacity: 0;
/* #ifndef APP-NVUE */
z-index: 1;
/* #endif */
}
/* #ifndef APP-NVUE */
.fui-fab__opentype-btn::after {
border-width: 0;
}
/* #endif */
</style>
+126
View File
@@ -0,0 +1,126 @@
// 本文件由FirstUI授权予佛山市航电梦联网络科技有限公司(会员ID:112 7,营业执照号:91 4 4 0 605 MA 556H1KX H)专用,请尊重知识产权,勿私下传播,违者追究法律责任。
var movable = {
width: 100,
height: 100,
disabled: false,
left: 0,
top: 0,
app: false
}
function isPC() {
if (typeof navigator !== 'object') return false;
var userAgentInfo = navigator.userAgent;
var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
var flag = true;
for (var v = 0; v < Agents.length - 1; v++) {
if (userAgentInfo.indexOf(Agents[v]) > 0) {
flag = false;
break;
}
}
return flag;
}
var isH5 = false
if (typeof window === 'object') isH5 = true
function setInitValue(dataset) {
movable.width = +dataset.width
movable.height = +dataset.height
movable.top = +dataset.top
movable.left = +dataset.left
//H5获取bool值为undefined
movable.disabled = (+dataset.disabled) == 1 ? true : false
movable.app = (+dataset.app) == 1 ? true : false
}
function touchstart(e, ins) {
var state = e.instance.getState()
var touch = e.touches[0] || e.changedTouches[0];
if (isH5 && isPC()) {
touch = e;
}
var dataset = e.instance.getDataset()
state.startX = touch.clientX
state.startY = touch.clientY
setInitValue(dataset)
}
function styleChange(left, top, ins) {
if (!ins) return;
var mview = ins.selectComponent('.fui-fab__btn-wrap');
if (!mview) return;
mview.setStyle({
transform: 'translate3d(' + left + 'px,' + top + 'px,0)'
})
}
function touchmove(e, ins, events) {
if (movable.disabled) return;
if (e.preventDefault) {
e.preventDefault()
}
if (movable.app && event && event.preventDefault && event.cancelable) {
event.preventDefault()
}
var state = {}
var touch = {}
if (isH5 && isPC()) {
touch = e;
if (events && events.instance) {
state = events.instance.getState()
}
} else {
touch = e.touches[0] || e.changedTouches[0]
state = e.instance.getState()
}
var pageX = touch.clientX;
var pageY = touch.clientY;
var left = pageX - state.startX + (state.lastLeft || 0);
left = left < -movable.left ? -movable.left : left;
left = left > movable.width ? movable.width : left;
state.startX = pageX
var top = pageY - state.startY + (state.lastTop || 0);
top = top < -movable.top ? -movable.top : top;
top = top > movable.height ? movable.height : top;
state.startY = pageY
state.lastLeft = left
state.lastTop = top
styleChange(left, top, ins)
}
var _movable = false;
function mousedown(e, ins) {
if (!isH5 || !isPC()) return
touchstart(e, ins)
_movable = true
window.onmousemove = function(event) {
if (!isH5 || !isPC() || !_movable) return
touchmove(event, ins, e)
}
window.onmouseup = function(event) {
if (!isH5 || !isPC() || !_movable) return
_movable = false
}
}
function fabreset(reset, oldreset, owner, ins) {
if (reset > 0) {
var state = ins.getState()
state.startY = 0;
state.startX = 0;
state.lastLeft = 0;
state.lastTop = 0;
styleChange(0, 0, owner)
}
}
module.exports = {
touchstart: touchstart,
touchmove: touchmove,
mousedown: mousedown,
fabreset: fabreset
}
+59
View File
@@ -0,0 +1,59 @@
// 本文件由FirstUI授权予佛山市航电梦联网络科技有限公司(会员ID: 1127,营业执照号: 9 1 4 40 6 0 5 M A 556H1 K X H)专用,请尊重知识产权,勿私下传播,违者追究法律责任。
// #ifndef APP-PLUS || MP-WEIXIN || H5
export default {
data() {
return {
startX: 0,
startY: 0,
lastLeft: 0,
lastTop: 0,
transform: ''
}
},
watch: {
resetNum(val) {
if (val > 0) {
this.startX = 0;
this.startY = 0;
this.lastLeft = 0;
this.lastTop = 0;
this.transform = 'translate3d(0,0,0)'
}
}
},
methods: {
touchstart(e) {
if (!this.isDrag) return;
const touch = e.touches || e.changedTouches
this.startX = touch[0].clientX
this.startY = touch[0].clientY
},
touchmove(e) {
if (!this.isDrag) return;
const touch = e.touches || e.changedTouches
let pageX = touch[0].clientX,
pageY = touch[0].clientY;
var left = pageX - this.startX + this.lastLeft;
left = left < -this.eLeft ? -this.eLeft : left;
left = left > this.maxWidth ? this.maxWidth : left;
this.startX = pageX
var top = pageY - this.startY + this.lastTop;
top = top < -this.eTop ? -this.eTop : top;
top = top > this.maxHeight ? this.maxHeight : top;
this.startY = pageY
this.lastLeft = left
this.lastTop = top
this.transform = `translate3d(${left}px,${top}px,0)`
}
}
}
// #endif
// #ifdef APP-PLUS|| MP-WEIXIN || H5
export default {}
// #endif
@@ -0,0 +1,232 @@
<template>
<!--本文件由FirstUI授权予佛山市航电梦联网络科技有限公司会员ID 112 7营业执照号 91 440 605 MA5 5 6 H1 K XH专用请尊重知识产权勿私下传播违者追究法律责任-->
<view class="fui-footer" :class="[isFixed?'fui-footer__fixed-bottom':'']"
:style="{background:background,bottom:bottom+'rpx'}">
<view class="fui-footer__link" v-if="navigate.length>0">
<navigator v-for="(item,index) in navigate" :key="index" class="fui-link__item" hover-class="fui-link-hover"
hover-stop-propagation :open-type="item.openType || 'navigate'" :url="item.url" :delta="item.delta">
<text class="fui-link__text"
:class="{'fui-link__color':!item.color,'fui-link__text-border':index===navigate.length-1}"
:style="{color:item.color || linkColor,fontSize:(item.size || 28)+'rpx',borderColor:borderColor,lineHeight:(item.size || 28)+'rpx'}">{{item.text}}</text>
</navigator>
</view>
<view class="fui-footer__text"
:class="{'fui-as__safe-weex':iphoneX && safeArea,'fui-footer__safearea':safeArea}">
<text :style="{color:color,fontSize:size+'rpx'}">{{text}}</text>
</view>
</view>
</template>
<script>
export default {
name: "fui-footer",
props: {
//urlopenTypedelta textcolorsize
//链接设置 object数据格式对应上面注释的属性值
navigate: {
type: Array,
default: function() {
return []
}
},
//底部文本
text: {
type: String,
default: ''
},
//文本字体颜色
color: {
type: String,
default: "#B2B2B2"
},
//文本字体大小
size: {
type: [Number, String],
default: 24
},
//footer背景颜色
background: {
type: String,
default: "transparent"
},
//分隔线颜色,仅nvue生效
borderColor: {
type: String,
default: '#B2B2B2'
},
//是否固定在底部
isFixed: {
type: Boolean,
default: false
},
bottom: {
type: [Number, String],
default: 0
},
//是否适配底部安全区
safeArea: {
type: Boolean,
default: true
}
},
computed: {
linkColor() {
const app = uni && uni.$fui && uni.$fui.color;
return (app && app.link) || '#465CFF';
}
},
data() {
return {
iphoneX: false
}
},
created() {
// #ifdef APP-NVUE || MP-TOUTIAO
this.iphoneX = this.isPhoneX();
// #endif
},
methods: {
// #ifdef APP-NVUE || MP-TOUTIAO
isPhoneX() {
if (!this.safeArea) return false;
//34px
const res = uni.getSystemInfoSync();
let iphonex = false;
let models = ['iphonex', 'iphonexr', 'iphonexsmax']
for (let i = 11; i < 20; i++) {
models.push(`iphone${i}`)
models.push(`iphone${i}mini`)
models.push(`iphone${i}pro`)
models.push(`iphone${i}promax`)
}
const model = res.model.replace(/\s/g, "").toLowerCase()
const newModel = model.split('<')[0]
if (models.includes(model) || models.includes(newModel) || (res.safeAreaInsets && res.safeAreaInsets
.bottom > 0)) {
iphonex = true;
}
return iphonex;
}
// #endif
}
}
</script>
<style scoped>
.fui-footer {
flex: 1;
/* #ifndef APP-NVUE */
width: 100%;
box-sizing: border-box;
word-break: break-all;
/* #endif */
overflow: hidden;
padding-top: 32rpx;
padding-bottom: 32rpx;
padding-left: 32rpx;
padding-right: 32rpx;
}
.fui-footer__fixed-bottom {
position: fixed;
z-index: 99;
left: 0;
right: 0;
/* #ifndef APP-NVUE */
left: constant(safe-area-inset-left);
left: env(safe-area-inset-left);
right: constant(safe-area-inset-right);
right: env(safe-area-inset-right)
/* #endif */
}
.fui-footer__link {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
font-size: 28rpx;
}
/* #ifndef APP-NVUE */
.fui-link__color {
color: var(--fui-color-link, #465CFF) !important;
}
/* #endif */
.fui-link__item {
position: relative;
line-height: 1;
}
.fui-link__text {
padding: 0 18rpx;
/* #ifdef APP-NVUE */
border-right-width: 0.5px;
border-right-style: solid;
/* #endif */
font-weight: 400;
}
.fui-link__text-border {
border-right-width: 0;
}
/* #ifndef APP-NVUE */
.fui-link__item::before {
content: " ";
position: absolute;
right: 0;
top: 4rpx;
width: 1px;
bottom: 4rpx;
border-right: 1px solid var(--fui-color-label, #B2B2B2);
-webkit-transform-origin: 100% 0;
transform-origin: 100% 0;
-webkit-transform: scaleX(0.5);
transform: scaleX(0.5);
}
.fui-link__item:last-child::before {
border-right: 0 !important
}
/* #endif */
.fui-link-hover {
opacity: 0.5
}
.fui-footer__text {
flex: 1;
/* #ifdef APP-NVUE */
flex-direction: row;
justify-content: center;
flex-wrap: wrap;
/* #endif */
line-height: 1;
text-align: center;
padding-top: 8rpx;
font-weight: 400;
}
/* #ifndef APP-NVUE || MP-TOUTIAO */
.fui-footer__safearea {
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
/* #endif */
/* #ifdef APP-NVUE || MP-TOUTIAO */
.fui-as__safe-weex {
padding-bottom: 34px;
}
/* #endif */
</style>
+166
View File
@@ -0,0 +1,166 @@
// 本文件由FirstUI授权予佛山市航电梦联网络科技有限公司(会员ID:1 1 27,营业执照号: 91 4 4 0 605 M A 55 6H1KX H)专用,请尊重知识产权,勿私下传播,违者追究法律责任。
export default {
"addressbook":"\ue80c",
"addfriends-fill": "\ue80a",
"addfriends": "\ue80b",
"backspace-fill": "\ue808",
"backspace": "\ue809",
"bankcard-fill": "\ue806",
"bankcard": "\ue807",
"camera-fill": "\ue804",
"camera": "\ue805",
"captcha-fill": "\ue802",
"captcha": "\ue803",
"cart-fill": "\ue800",
"cart": "\ue801",
"classify": "\ue7fe",
"classify-fill": "\ue7ff",
"comment-fill": "\ue7fc",
"comment": "\ue7fd",
"community-fill": "\ue7fa",
"community": "\ue7fb",
"coupon-fill": "\ue7f8",
"coupon": "\ue7f9",
"delete": "\ue7f6",
"delete-fill": "\ue7f7",
"edit": "\ue7f4",
"edit-fill": "\ue7f5",
"fabulous-fill": "\ue7f2",
"fabulous": "\ue7f3",
"find": "\ue7f0",
"find-fill": "\ue7f1",
"help-fill": "\ue7ee",
"help": "\ue7ef",
"home-fill": "\ue7ec",
"home": "\ue7ed",
"idcard-fill": "\ue7ea",
"idcard": "\ue7eb",
"info": "\ue7e8",
"info-fill": "\ue7e9",
"invite-fill": "\ue7e6",
"invite": "\ue7e7",
"kefu-fill": "\ue7e4",
"kefu": "\ue7e5",
"like-fill": "\ue7e2",
"like": "\ue7e3",
"location": "\ue7e0",
"location-fill": "\ue7e1",
"lock": "\ue7de",
"lock-fill": "\ue7df",
"mail-fill": "\ue7dc",
"mail": "\ue7dd",
"message": "\ue7da",
"message-fill": "\ue7db",
"mobile-fill": "\ue7d8",
"mobile": "\ue7d9",
"more": "\ue7d6",
"more-fill": "\ue7d7",
"my-fill": "\ue7d4",
"my": "\ue7d5",
"principal":"\ue80d",
"notice-fill": "\ue7d2",
"notice": "\ue7d3",
"order": "\ue7d0",
"order-fill": "\ue7d1",
"picture": "\ue7ce",
"picture-fill": "\ue7cf",
"setup-fill": "\ue7cc",
"setup": "\ue7cd",
"share": "\ue7ca",
"share-fill": "\ue7cb",
"shop": "\ue7c8",
"shop-fill": "\ue7c9",
"star-fill": "\ue7c5",
"star": "\ue7c6",
"starhalf": "\ue7c7",
"stepon-fill": "\ue7c3",
"stepon": "\ue7c4",
"wait-fill": "\ue7c1",
"wait": "\ue7c2",
"warning": "\ue7bf",
"warning-fill": "\ue7c0",
"plus": "\ue7bc",
"plussign-fill": "\ue7bd",
"plussign": "\ue7be",
"minus": "\ue7b9",
"minussign": "\ue7ba",
"minussign-fill": "\ue7bb",
"close": "\ue7b8",
"clear": "\ue7b6",
"clear-fill": "\ue7b7",
"checkbox-fill": "\ue7b5",
"checkround": "\ue7b4",
"checkbox": "\ue7b3",
"check": "\ue7b2",
"pulldown-fill": "\ue7ae",
"pullup": "\ue7af",
"pullup-fill": "\ue7b0",
"pulldown": "\ue7b1",
"roundright-fill": "\ue7ac",
"roundright": "\ue7ad",
"arrowright": "\ue7a9",
"arrowleft": "\ue7aa",
"arrowdown": "\ue7ab",
"left": "\ue7a6",
"up": "\ue7a7",
"right": "\ue7a8",
"back": "\ue7a3",
"top": "\ue7a4",
"dropdown": "\ue7a5",
"turningleft": "\ue79f",
"turningup": "\ue7a0",
"turningright": "\ue7a1",
"turningdown": "\ue7a2",
"refresh": "\ue79c",
"loading": "\ue79d",
"search": "\ue79e",
"rotate": "\ue79b",
"screen": "\ue79a",
"signin": "\ue799",
"calendar": "\ue798",
"scan": "\ue797",
"qrcode": "\ue796",
"wallet": "\ue795",
"telephone": "\ue794",
"visible": "\ue793",
"invisible": "\ue792",
"menu": "\ue78e",
"operate": "\ue78f",
"slide": "\ue790",
"list": "\ue791",
"nonetwork": "\ue78d",
"partake": "\ue78c",
"qa": "\ue78b",
"barchart": "\ue788",
"piechart": "\ue789",
"linechart": "\ue78a",
"at": "\ue787",
"face": "\ue77f",
"redpacket": "\ue780",
"suspend": "\ue781",
"link": "\ue782",
"keyboard": "\ue783",
"play": "\ue784",
"video": "\ue785",
"voice": "\ue786",
"sina": "\ue77a",
"browser": "\ue77b",
"moments": "\ue77c",
"qq": "\ue77d",
"wechat": "\ue77e",
"balance": "\ue779",
"bankcardpay": "\ue778",
"wxpay": "\ue777",
"alipay": "\ue776",
"payment":"\ue818",
"receive":"\ue817",
"sendout":"\ue816",
"evaluate":"\ue815",
"aftersale":"\ue814",
"warehouse":"\ue813",
"transport":"\ue812",
"delivery":"\ue811",
"switch":"\ue810",
"goods":"\ue80f",
"goods-fill":"\ue80e"
}
Binary file not shown.
@@ -0,0 +1,151 @@
<template>
<!--本文件由FirstUI授权予佛山市航电梦联网络科技有限公司会员ID 112 7营业执照号9 144 0 6 0 5 MA5 56H1K XH专用请尊重知识产权勿私下传播违者追究法律责任-->
<!-- #ifndef APP-NVUE -->
<text :style="{ color:getColor, fontSize: getSize, fontWeight: fontWeight}" class="fui-icon"
:class="[!getColor && !primary?'fui-icon__color':'',primary && (!color || color===true)?'fui-icon__active-color':'',disabled?'fui-icon__not-allowed':'',customPrefix && customPrefix!==true?customPrefix:'',customPrefix && customPrefix!==true?name:'']"
@click="handleClick">{{ icons[name] || '' }}</text>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<text
:style="{ color: primary && (!color || color===true)?primaryColor:getColor, fontSize: getSize,lineHeight:getSize, fontWeight: fontWeight}"
class="fui-icon" :class="[customPrefix && customPrefix!==true?customPrefix:'']"
@click="handleClick">{{ customPrefix && customPrefix!==true?name:icons[name] }}</text>
<!-- #endif -->
</template>
<script>
import icons from './fui-icon.js';
// #ifdef APP-NVUE
var domModule = weex.requireModule('dom');
import fuiicons from './fui-icon.ttf'
domModule.addRule('fontFace', {
'fontFamily': 'fuiFont',
'src': "url('" + fuiicons + "')"
});
// #endif
export default {
name: "fui-icon",
emits: ['click'],
// #ifdef MP-WEIXIN
options: {
addGlobalClass: true
},
// #endif
props: {
name: {
type: String,
default: ''
},
size: {
type: [Number, String],
default: 0
},
//rpx | px
unit: {
type: String,
default: ''
},
color: {
type: String,
default: ''
},
//字重
fontWeight: {
type: [Number, String],
default: 'normal'
},
//是否禁用点击
disabled: {
type: Boolean,
default: false
},
params: {
type: [Number, String],
default: 0
},
customPrefix: {
type: String,
default: ''
},
//是否显示为主色调,color为空时有效。【内部使用】
primary: {
type: Boolean,
default: false
}
},
computed: {
getSize() {
const size = (uni.$fui && uni.$fui.fuiIcon && uni.$fui.fuiIcon.size) || 64
const unit = (uni.$fui && uni.$fui.fuiIcon && uni.$fui.fuiIcon.unit) || 'rpx'
return (this.size || size) + (this.unit || unit)
},
primaryColor() {
const app = uni && uni.$fui && uni.$fui.color;
return (app && app.primary) || '#465CFF';
},
getColor() {
const app = uni && uni.$fui && uni.$fui.fuiIcon;
let color = this.color;
if (!color || (color && color === true)) {
color = (app && app.color)
}
// #ifdef APP-NVUE
if (!color || color === true) {
color = '#333333'
}
// #endif
return color;
}
},
data() {
return {
icons: icons
};
},
methods: {
handleClick() {
if (this.disabled) return;
this.$emit('click', {
params: this.params
});
}
}
}
</script>
<style scoped>
/* #ifndef APP-NVUE */
/* 头条小程序组件内不能引入字体,需要在父级页面引入字体文件*/
@font-face {
font-family: fuiFont;
src: url("./fui-icon.ttf") format("truetype");
}
/* #endif */
.fui-icon {
font-family: fuiFont;
text-decoration: none;
text-align: center;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
}
/* #ifndef APP-NVUE */
.fui-icon__color {
color: var(--fui-color-section, #333333) !important;
}
.fui-icon__active-color {
color: var(--fui-color-primary, #465CFF) !important;
}
/* #endif */
.fui-icon__not-allowed {
/* #ifdef H5 */
cursor: not-allowed !important;
/* #endif */
}
</style>
@@ -0,0 +1,524 @@
<template>
<!--本文件由FirstUI授权予佛山市航电梦联网络科技有限公司会员ID112 7营业执照号9 14 406 0 5MA5 5 6 H 1KXH专用请尊重知识产权勿私下传播违者追究法律责任-->
<view class="fui-load__ani" :class="{'fui-load__ani-fixed':isFixed}"
:style="{background:isFixed?maskColor:'transparent'}">
<!-- #ifndef APP-NVUE -->
<view class="fui-load__ani-1" v-if="type==1">
<view class="fui-load__ani-a" :class="{'fui-load__ani-bg':!color || color===true}"
:style="{background:color}"></view>
<view class="fui-ani__ani-b" :class="{'fui-load__ani-bg':!color || color===true}"
:style="{background:color}"></view>
<view :class="{'fui-load__ani-bg':!color || color==='true'}" :style="{background:color}"></view>
</view>
<view class="fui-load__ani-2" v-if="type==2">
<view class="fui-load__ani-line fui-load__ani-c" :class="{'fui-load__ani-bg':!color || color===true}"
:style="{background:color}"></view>
<view class="fui-load__ani-line fui-load__ani-d" :class="{'fui-load__ani-bg':!color || color===true}"
:style="{background:color}"></view>
<view class="fui-load__ani-line fui-load__ani-e" :class="{'fui-load__ani-bg':!color || color===true}"
:style="{background:color}"></view>
</view>
<view class="fui-load__ani-3" :class="{'fui-load__ani-border':!color || color===true}"
:style="{'border-left-color':color,'border-right-color':color}" v-if="type==3">
</view>
<view class="fui-load__ani-4" :class="{'fui-load__ani-bcolor':!color || color===true}"
:style="{borderColor:color}" v-if="type==4">
<view class="fui-load__ani-f" :class="{'fui-load__ani-bg':!color || color===true}"
:style="{background:color}"></view>
</view>
<view class="fui-load__ani-5" :class="{'fui-load__ani-bg':!color || color===true}" :style="{background:color}"
v-if="type==5">
<view class="fui-load__ani-g" :class="{'fui-load__ani-bg':!color || color===true}"
:style="{background:color}"></view>
<view class="fui-load__ani-h" :class="{'fui-load__ani-bg':!color || color===true}"
:style="{background:color}"></view>
</view>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<view class="fui-load__ani-1n" v-if="type==1">
<view class="fui-loadani__1" :style="{background:color}" ref="ani_11"></view>
<view class="fui-loadani__1" :style="{background:color}" ref="ani_12"></view>
<view class="fui-loadani__1" ref="ani_13" :style="{background:color}"></view>
</view>
<view class="fui-load__ani-2n" v-if="type==2">
<view class="fui-load__ani-line" ref="ani_11" :style="{background:color}"></view>
<view class="fui-load__ani-line" ref="ani_12" :style="{background:color}"></view>
<view class="fui-load__ani-line" ref="ani_13" :style="{background:color}"></view>
</view>
<view class="fui-load__ani-3n" ref="fui_loadani34"
:style="{'border-left-color':color,'border-right-color':color}" v-if="type==3">
</view>
<view class="fui-load__ani-4n" ref="fui_loadani34" v-if="type==4">
<view class="fui-load__ani-41" :style="{borderColor:color}"></view>
<view class="fui-load__ani-42" :style="{background:color}"></view>
</view>
<view class="fui-load__ani-5n" v-if="type==5">
<view class="fui-load__ani-i" ref="ani_11" :style="{background:color}"></view>
<view class="fui-load__ani-i" ref="ani_12" :style="{background:color}"></view>
<view class="fui-load__ani-i" ref="ani_13" :style="{background:color}"></view>
</view>
<!-- #endif -->
</view>
</template>
<script>
// #ifdef APP-NVUE
const animation = weex.requireModule('animation')
// #endif
export default {
name: "fui-load-ani",
props: {
//loading 类型:1~5
type: {
type: [Number, String],
default: 1
},
//loading颜色
// #ifdef APP-NVUE
color: {
type: String,
default: '#465CFF'
},
// #endif
// #ifndef APP-NVUE
color: {
type: String,
default: ''
},
// #endif
//是否固定在屏幕中间显示
isFixed: {
type: Boolean,
default: false
},
//isFixed=true时有效
maskColor: {
type: String,
default: 'transparent'
}
},
// #ifdef APP-NVUE
created() {
this.timer = null;
this.deg = 0;
this.stop = false;
},
mounted() {
clearInterval(this.timer)
this.$nextTick(() => {
if (this.type == 1 || this.type == 2 || this.type == 5) {
setTimeout(() => {
this.startAni()
}, 50)
this.timer = setInterval(() => {
this.startAni()
}, 1200)
} else if (this.type == 3 || this.type == 4) {
setTimeout(() => {
this.deg += 360;
this._animation()
}, 50)
}
})
},
// #ifndef VUE3
beforeDestroy() {
clearInterval(this.timer)
this.deg = 0;
this.stop = true;
},
// #endif
// #ifdef VUE3
beforeUnmount() {
clearInterval(this.timer)
this.deg = 0;
this.stop = true;
},
// #endif
methods: {
transition(el, options, duration, delay = 0) {
return new Promise((resolve) => {
animation.transition(el.ref, {
duration,
delay,
timingFunction: 'linear',
needLayout: false,
...options,
}, resolve)
})
},
ani(el) {
let styles = {}
let styles2 = {}
if (this.type == 5) {
styles.opacity = 0.25
styles2.opacity = 1
} else {
styles.transform = this.type == 1 ? 'scale(0)' : `translateY(0)`
styles2.transform = this.type == 1 ? 'scale(1)' : `translateY(100%)`
}
this.transition(el, {
styles
}, 500, 0).then(() => {
this.transition(el, {
styles: styles2
}, 500, 0)
})
},
startAni() {
this.ani(this.$refs['ani_11'])
setTimeout(() => {
this.ani(this.$refs['ani_12'])
}, 300)
setTimeout(() => {
this.ani(this.$refs['ani_13'])
}, 500)
},
_animation() {
if (!this.$refs['fui_loadani34'] || this.stop) return;
animation.transition(
this.$refs['fui_loadani34'].ref, {
styles: {
transform: `rotate(${this.deg}deg)`
},
duration: 800, //ms
timingFunction: 'linear',
iterationCount: 'infinite',
needLayout: false,
delay: 0 //ms
}, () => {
this.deg += 360;
this._animation()
}
);
}
}
// #endif
}
</script>
<style scoped>
.fui-load__ani {
/* #ifndef APP-NVUE */
width: 100%;
display: flex;
/* #endif */
/* #ifdef APP-VUE */
flex: 1;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
}
.fui-load__ani-fixed {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
/* #ifndef APP-NVUE */
display: flex;
z-index: 996;
/* #endif */
justify-content: center;
align-items: center;
}
/* #ifndef APP-NVUE */
.fui-load__ani-1{
flex-direction: row;
}
.fui-load__ani-1 view {
width: 36rpx;
height: 36rpx;
border-radius: 50%;
display: inline-block;
animation: ani_1 1.4s infinite ease-in-out;
animation-fill-mode: both;
}
.fui-load__ani-1 .fui-load__ani-a {
animation-delay: -0.30s;
}
.fui-load__ani-1 .fui-load__ani-b {
animation-delay: -0.15s;
}
@keyframes ani_1 {
0%,
80%,
100% {
-webkit-transform: scale(0)
}
40% {
-webkit-transform: scale(1)
}
}
.fui-load__ani-2 {
position: relative;
width: 60rpx;
height: 60rpx;
display: inline-block;
vertical-align: middle;
}
.fui-load__ani-line {
width: 8rpx;
position: absolute;
border-top-left-radius: 8rpx;
border-top-right-radius: 8rpx;
bottom: 0;
transform: translateZ(0);
}
.fui-load__ani-c {
animation: ani_2 0.5s ease alternate infinite;
}
.fui-load__ani-d {
left: 20rpx;
animation: ani_2 0.5s 0.2s ease alternate infinite;
}
.fui-load__ani-e {
left: 40rpx;
animation: ani_2 0.5s 0.4s ease alternate infinite;
}
@keyframes ani_2 {
0% {
height: 0;
}
100% {
height: 75%;
}
}
.fui-load__ani-3 {
width: 40rpx;
height: 40rpx;
border: 2px solid transparent;
border-radius: 50%;
animation: 0.9s ani_3 linear infinite;
}
@keyframes ani_3 {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.fui-load__ani-4 {
width: 52rpx;
height: 52rpx;
border: 1px solid;
border-radius: 50%;
-webkit-animation: 1s ani_3 linear infinite;
animation: .9s ani_3 linear infinite;
position: relative;
}
.fui-load__ani-f {
width: 16rpx;
height: 16rpx;
position: absolute;
top: -8rpx;
left: 50%;
border-radius: 50%;
opacity: .6;
}
.fui-load__ani-5 {
position: relative;
width: 16rpx;
height: 16rpx;
border-radius: 50%;
animation: ani_4 1s infinite linear;
}
.fui-load__ani-g,
.fui-load__ani-h {
display: inline-block;
position: absolute;
top: 0;
width: 16rpx;
height: 16rpx;
border-radius: 50%;
opacity: 0.25;
}
.fui-load__ani-g {
left: -30rpx;
animation: ani_4_2 1s infinite linear;
}
.fui-load__ani-h {
left: 30rpx;
animation: ani_4_1 1s infinite linear;
}
@-webkit-keyframes ani_4 {
0% {
opacity: 1;
}
33% {
opacity: 0.25;
}
66% {
opacity: 0.25;
}
100% {
opacity: 1;
}
}
@keyframes ani_4_1 {
0% {
opacity: 0.25;
}
33% {
opacity: 1;
}
66% {
opacity: 0.25;
}
}
@keyframes ani_4_2 {
33% {
opacity: 0.25;
}
66% {
opacity: 1;
}
100% {
opacity: 0.25;
}
}
.fui-load__ani-bcolor {
border-color: var(--fui-color-primary, #465CFF) !important;
}
.fui-load__ani-border {
border-left-color: var(--fui-color-primary, #465CFF) !important;
border-right-color: var(--fui-color-primary, #465CFF) !important;
}
.fui-load__ani-bg {
background: var(--fui-color-primary, #465CFF) !important;
}
/* #endif */
/* #ifdef APP-NVUE */
.fui-load__ani-1n {
flex-direction: row;
align-items: center;
}
.fui-loadani__1 {
width: 36rpx;
height: 36rpx;
border-radius: 36rpx;
}
.fui-load__ani-2n {
position: relative;
width: 56rpx;
height: 56rpx;
flex-direction: row;
align-items: flex-end;
justify-content: space-between;
overflow: hidden;
}
.fui-load__ani-line {
width: 8rpx;
height: 56rpx;
border-top-left-radius: 8rpx;
border-top-right-radius: 8rpx;
transform: translateY(100%);
}
.fui-load__ani-3n {
width: 40rpx;
height: 40rpx;
border: 2px solid transparent;
border-radius: 24rpx;
}
.fui-load__ani-4n {
width: 68rpx;
height: 68rpx;
position: relative;
align-items: center;
justify-content: center;
border: 0;
}
.fui-load__ani-41 {
width: 52rpx;
height: 52rpx;
border: 1px solid;
border-radius: 50%;
}
.fui-load__ani-42 {
width: 16rpx;
height: 16rpx;
position: absolute;
top: 0rpx;
left: 26rpx;
border-radius: 12rpx;
opacity: .6;
}
.fui-load__ani-5n {
width: 76rpx;
height: 18rpx;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
.fui-load__ani-i {
width: 16rpx;
height: 16rpx;
border-radius: 12rpx;
opacity: .25;
}
/* #endif */
</style>
@@ -0,0 +1,302 @@
<template>
<!--本文件由FirstUI授权予佛山市航电梦联网络科技有限公司会员ID1 1 27营业执照号 91 4 4 0 605 M A556 H 1KXH专用请尊重知识产权勿私下传播违者追究法律责任-->
<view :style="getStyle">
<view class="fui-nav__bar"
:class="{'fui-nav__bar-line':splitLine,'fui-nva__bar-bg':!background,'fui-nav__bar-fixed':isFixed}"
:style="{background:background,'border-bottom-color':lineColor,paddingLeft:padding+'px',paddingRight:padding+'px',zIndex:zIndex}">
<view class="fui-nav__status-bar" :style="{height:statusBarHeight+'px'}" v-if="statusBar"></view>
<view class="fui-nav__header" v-if="!custom">
<view class="fui-nav__left" @tap="leftClick">
<slot></slot>
</view>
<view class="fui-nav__title" v-if="title" @tap="titleClick">
<text class="fui-nav__title-text"
:style="{fontSize:size+'px',color:color,fontWeight:fontWeight}">{{title}}</text>
</view>
<view class="fui-nav__right" @tap="rightClick">
<slot name="right"></slot>
</view>
</view>
<view class="fui-nav__header" v-if="custom">
<slot></slot>
</view>
</view>
</view>
</template>
<script>
var sys = uni.getSystemInfoSync();
export default {
name: "fui-nav-bar",
emits: ['init', 'leftClick', 'rightClick', 'titleClick'],
props: {
//navbar左右padding值,单位px
padding: {
type: [Number, String],
default: 8
},
//标题
title: {
type: String,
default: ''
},
//标题字体大小,单位px
// #ifdef H5
size: {
type: [Number, String],
default: 16
},
// #endif
// #ifndef H5
size: {
type: [Number, String],
default: 17
},
// #endif
//标题颜色
// #ifdef APP-NVUE
color: {
type: String,
default: '#181818'
},
// #endif
// #ifndef APP-NVUE
color: {
type: String,
default: ''
},
// #endif
fontWeight: {
type: [Number, String],
default: 500
},
// #ifdef APP-NVUE
background: {
type: String,
default: '#fff'
},
// #endif
// #ifndef APP-NVUE
background: {
type: String,
default: ''
},
// #endif
//是否需要底部分割线
splitLine: {
type: Boolean,
default: false
},
//分割线颜色,仅Nvue生效
lineColor: {
type: String,
default: '#eee'
},
//是否包含状态栏
statusBar: {
type: Boolean,
default: true
},
//是否固定在顶部
isFixed: {
type: Boolean,
default: false
},
//z-index
zIndex: {
type: [Number, String],
default: 996
},
//自定义navbar内容,title、右插槽失效
custom: {
type: Boolean,
default: false
},
//v1.9.9+
isOccupy: {
type: Boolean,
default: false
}
},
computed: {
getStyle() {
let style = ''
if (this.isOccupy) {
let height = this.statusBar ? (this.statusBarHeight + 44) : 44
style += `height:${height}px;`
}
return style
}
},
data() {
return {
statusBarHeight: sys.statusBarHeight
};
},
created() {
let obj = {};
// #ifdef MP-WEIXIN || MP-QQ || MP-BAIDU || MP-TOUTIAO
obj = uni.getMenuButtonBoundingClientRect();
// #endif
// #ifdef MP-ALIPAY
my.hideAddToDesktopMenu();
// #endif
this.$emit('init', {
windowWidth: sys.windowWidth,
//不包含状态栏高度固定为:44px
height: 44,
statusBarHeight: this.statusBarHeight,
//小程序右上角悬浮按钮左边界坐标,单位:px
left: obj.left || -1,
//小程序右上角悬浮按钮宽度,单位:px
btnWidth: obj.width || 0,
//小程序右上角悬浮按钮高度,单位:px
btnHeight: obj.height || 0
})
},
methods: {
leftClick() {
this.$emit("leftClick");
},
rightClick() {
this.$emit("rightClick");
},
titleClick() {
this.$emit("titleClick");
}
}
}
</script>
<style scoped>
.fui-nav__status-bar {
/* #ifdef APP-NVUE */
width: 750rpx;
/* #endif */
/* #ifndef APP-NVUE */
width: 100%;
box-sizing: border-box;
/* #endif */
}
.fui-nav__header {
height: 44px;
/* #ifndef APP-NVUE */
width: 100%;
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: space-between;
overflow: hidden;
}
.fui-nav__bar {
flex: 1;
/* #ifndef APP-NVUE */
width: 100%;
box-sizing: border-box;
/* #endif */
}
/* #ifndef APP-NVUE */
.fui-nva__bar-bg {
background: var(--fui-bg-color, #fff) !important;
}
/* #endif */
.fui-nav__bar-line {
position: relative;
/* #ifdef APP-NVUE */
border-bottom: 0.5px;
border-bottom-style: solid;
/* #endif */
/* #ifndef APP-NVUE */
border-bottom: 0;
/* #endif */
}
/* #ifndef APP-NVUE */
.fui-nav__bar-line::after {
content: '';
position: absolute;
border-bottom: 1px solid var(--fui-color-border, #EEEEEE) !important;
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
transform-origin: 0 100%;
bottom: 0;
right: 0;
left: 0;
}
/* #endif */
.fui-nav__left {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
width: 150rpx;
justify-content: flex-start;
align-items: center;
}
.fui-nav__right {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
width: 150rpx;
justify-content: flex-end;
align-items: center;
}
.fui-nav__title {
flex: 1;
/* #ifndef APP-NVUE */
display: flex;
box-sizing: border-box;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
overflow: hidden;
padding: 0 30rpx;
}
/* #ifndef APP-NVUE */
.fui-nav__title-color {
color: var(--fui-color-title, #181818) !important;
}
/* #endif */
.fui-nav__title-text {
/* #ifdef APP-NVUE */
lines: 1;
/* #endif */
/* #ifndef APP-NVUE */
display: block;
overflow: hidden;
white-space: nowrap;
/* #endif */
text-overflow: ellipsis;
}
.fui-nav__bar-fixed {
position: fixed;
/* #ifdef H5 */
left: var(--window-left);
right: var(--window-right);
/* #endif */
/* #ifndef H5 */
left: 0;
right: 0;
/* #endif */
top: 0;
}
</style>
@@ -0,0 +1,51 @@
<template>
<!--本文件由FirstUI授权予佛山市航电梦联网络科技有限公司会员ID1 1 2 7营业执照号 9144 06 0 5 MA 5 56 H1K X H专用请尊重知识产权勿私下传播违者追究法律责任-->
<view class="fui-parse__group">
<slot></slot>
</view>
</template>
<script>
export default {
name: "fui-parse-group",
emits: ['atap', 'preview'],
provide() {
return {
parsegroup: this
}
},
props: {
imgPreview: {
type: Boolean,
default: true
},
thBgcolor: {
type: Boolean,
default: true
}
},
data() {
const pageNodeKey = `fui_parse_${Math.ceil(Math.random() * 10e5).toString(36)}`
return {
pageNodeKey
}
},
methods: {
onATap(href) {
this.$emit('atap', href)
},
previewImage(src, imageUrls) {
this.$emit('preview', {
src,
imageUrls
})
}
},
}
</script>
<style scoped>
.fui-parse__group {
width: 100%;
}
</style>
File diff suppressed because one or more lines are too long
@@ -0,0 +1,215 @@
<template>
<!--本文件由FirstUI授权予佛山市航电梦联网络科技有限公司会员ID 1 1 27营业执照号 9 144 0 605 M A 5 56 H1KX H专用请尊重知识产权勿私下传播违者追究法律责任-->
<view class="hljs">
<rich-text :nodes="code" space="nbsp"></rich-text>
</view>
</template>
<script>
import hljs from './high-light/index.js'
// 支持的解析语言列表
const LANGUAGE_LIST = [
'javascript',
'css',
'xml',
'sql',
'typescript',
'markdown',
'c++',
'c',
];
export default {
name: "firstui-audio",
props: {
codeText: {
type: String,
default: ''
},
language: {
type: String,
default: 'javascript'
}
},
data() {
return {
code: ''
};
},
created() {
this.parseCode(this.codeText, this.language)
},
methods: {
parseCode(input, language) {
const lang = LANGUAGE_LIST.includes(language) ? language : 'javascript'
const {
value
} = hljs.highlight(lang, input)
const highlighted = value.replace('&amp;', '&').trim()
let codeResult = `<code class="${lang}">${highlighted}</code>`
codeResult = codeResult.replace(/\n/g, "<br/>").replace('\<code\>', '')
this.code = codeResult;
}
}
}
</script>
<style>
/*
Style with support for rainbow parens
*/
.hljs {
/* #ifndef APP-NVUE */
display: block;
/* #endif */
overflow-x: auto;
padding: 0.5em;
background: #282c34;
color: #d1d9e1;
}
.hljs-comment,
.hljs-quote {
color: #969896;
font-style: italic;
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-literal,
.hljs-type,
.hljs-addition {
color: #cc99cc;
}
.hljs-number,
.hljs-selector-attr,
.hljs-selector-pseudo {
color: #f99157;
}
.hljs-string,
.hljs-doctag,
.hljs-regexp {
color: #8abeb7;
}
.hljs-title,
.hljs-name,
.hljs-section,
.hljs-built_in {
color: #b5bd68;
}
.hljs-variable,
.hljs-template-variable,
.hljs-selector-id,
.hljs-class .hljs-title {
color: #ffcc66;
}
.hljs-section,
.hljs-name,
.hljs-strong {
font-weight: bold;
}
.hljs-symbol,
.hljs-bullet,
.hljs-subst,
.hljs-meta,
.hljs-link {
color: #f99157;
}
.hljs-deletion {
color: #dc322f;
}
.hljs-formula {
background: #eee8d5;
}
.hljs-attr,
.hljs-attribute {
color: #81a2be;
}
.hljs-emphasis {
font-style: italic;
}
/* #ifdef VUE3 */
:deep(.hljs-comment),
:deep(.hljs-quote){
color: #969896;
font-style: italic;
}
:deep(.hljs-keyword),
:deep(.hljs-selector-tag),
:deep(.hljs-literal),
:deep(.hljs-type),
:deep(.hljs-addition) {
color: #cc99cc;
}
:deep(.hljs-number),
:deep(.hljs-selector-attr),
:deep(.hljs-selector-pseudo) {
color: #f99157;
}
:deep(.hljs-string),
:deep(.hljs-doctag),
:deep(.hljs-regexp) {
color: #8abeb7;
}
:deep(.hljs-title),
:deep(.hljs-name),
:deep(.hljs-section),
:deep(.hljs-built_in) {
color: #b5bd68;
}
:deep(.hljs-variable),
:deep(.hljs-template-variable),
:deep(.hljs-selector-id),
:deep(.hljs-class .hljs-title) {
color: #ffcc66;
}
:deep(.hljs-section),
:deep(.hljs-name),
:deep(.hljs-strong) {
font-weight: bold;
}
:deep(.hljs-symbol),
:deep(.hljs-bullet),
:deep(.hljs-subst),
:deep(.hljs-meta),
:deep(.hljs-link) {
color: #f99157;
}
:deep(.hljs-deletion) {
color: #dc322f;
}
:deep(.hljs-formula) {
background: #eee8d5;
}
:deep(.hljs-attr),
:deep(.hljs-attribute) {
color: #81a2be;
}
:deep(.hljs-emphasis) {
font-style: italic;
}
/* #endif */
</style>
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,26 @@
// 本文件由FirstUI授权予佛山市航电梦联网络科技有限公司(会员ID:1 1 27,营业执照号:9144 0 60 5M A5 5 6H1 K X H)专用,请尊重知识产权,勿私下传播,违者追究法律责任。
/**
* Modified by: firstui
* organization: FirstUI(https://www.firstui.cn/)
*/
import hljs from './highlight.code.js'
import javascript from './languages/javascript.js'
import css from './languages/css.js'
import xml from './languages/xml.js'
import sql from './languages/sql.js'
import typescript from './languages/typescript.js'
import markdown from './languages/markdown.js'
import cpp from './languages/cpp.js'
import c from './languages/c.js'
hljs.registerLanguage('javascript', javascript);
hljs.registerLanguage('css', css);
hljs.registerLanguage('xml', xml);
hljs.registerLanguage('sql', sql);
hljs.registerLanguage('typescript', typescript);
hljs.registerLanguage('markdown', markdown);
hljs.registerLanguage('c++', cpp);
hljs.registerLanguage('c', c);
export default hljs;
@@ -0,0 +1,287 @@
// 本文件由FirstUI授权予佛山市航电梦联网络科技有限公司(会员ID: 1 127,营业执照号: 9 1 4406 0 5M A 5 56H1KX H)专用,请尊重知识产权,勿私下传播,违者追究法律责任。
/*
Language: C-like foundation grammar for C/C++ grammars
Author: Ivan Sagalaev <maniac@softwaremaniacs.org>
Contributors: Evgeny Stepanischev <imbolk@gmail.com>, Zaven Muradyan <megalivoithos@gmail.com>, Roel Deckers <admin@codingcat.nl>, Sam Wu <samsam2310@gmail.com>, Jordi Petit <jordi.petit@gmail.com>, Pieter Vantorre <pietervantorre@gmail.com>, Google Inc. (David Benjamin) <davidben@google.com>
Modified by: firstui
organization: FirstUI(https://www.firstui.cn/)
*/
/* In the future the intention is to split out the C/C++ grammars distinctly
since they are separate languages. They will likely share a common foundation
though, and this file sets the groundwork for that - so that we get the breaking
change in v10 and don't have to change the requirements again later.
See: https://github.com/highlightjs/highlight.js/issues/2146
*/
import {
optional
} from '../regex.js'
/** @type LanguageFn */
export default function(hljs) {
// added for historic reasons because `hljs.C_LINE_COMMENT_MODE` does
// not include such support nor can we be sure all the grammars depending
// on it would desire this behavior
const C_LINE_COMMENT_MODE = hljs.COMMENT('//', '$', {
contains: [{
begin: /\\\n/
}]
});
const DECLTYPE_AUTO_RE = 'decltype\\(auto\\)';
const NAMESPACE_RE = '[a-zA-Z_]\\w*::';
const TEMPLATE_ARGUMENT_RE = '<[^<>]+>';
const FUNCTION_TYPE_RE = '(' +
DECLTYPE_AUTO_RE + '|' +
optional(NAMESPACE_RE) +
'[a-zA-Z_]\\w*' + optional(TEMPLATE_ARGUMENT_RE) +
')';
const CPP_PRIMITIVE_TYPES = {
className: 'keyword',
begin: '\\b[a-z\\d_]*_t\\b'
};
// https://en.cppreference.com/w/cpp/language/escape
// \\ \x \xFF \u2837 \u00323747 \374
const CHARACTER_ESCAPES = '\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)';
const STRINGS = {
className: 'string',
variants: [{
begin: '(u8?|U|L)?"',
end: '"',
illegal: '\\n',
contains: [hljs.BACKSLASH_ESCAPE]
},
{
begin: '(u8?|U|L)?\'(' + CHARACTER_ESCAPES + "|.)",
end: '\'',
illegal: '.'
},
// hljs.END_SAME_AS_BEGIN({
// begin: /(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,
// end: /\)([^()\\ ]{0,16})"/
// })
]
};
const NUMBERS = {
className: 'number',
variants: [{
begin: '\\b(0b[01\']+)'
},
{
begin: '(-?)\\b([\\d\']+(\\.[\\d\']*)?|\\.[\\d\']+)(u|U|l|L|ul|UL|f|F|b|B)'
},
{
begin: '(-?)(\\b0[xX][a-fA-F0-9\']+|(\\b[\\d\']+(\\.[\\d\']*)?|\\.[\\d\']+)([eE][-+]?[\\d\']+)?)'
}
],
relevance: 0
};
const PREPROCESSOR = {
className: 'meta',
begin: /#\s*[a-z]+\b/,
end: /$/,
keywords: {
'meta-keyword': 'if else elif endif define undef warning error line ' +
'pragma _Pragma ifdef ifndef include'
},
contains: [{
begin: /\\\n/,
relevance: 0
},
hljs.inherit(STRINGS, {
className: 'meta-string'
}),
{
className: 'meta-string',
begin: /<.*?>/,
end: /$/,
illegal: '\\n'
},
C_LINE_COMMENT_MODE,
hljs.C_BLOCK_COMMENT_MODE
]
};
const TITLE_MODE = {
className: 'title',
begin: optional(NAMESPACE_RE) + hljs.IDENT_RE,
relevance: 0
};
const FUNCTION_TITLE = optional(NAMESPACE_RE) + hljs.IDENT_RE + '\\s*\\(';
const CPP_KEYWORDS = {
keyword: 'int float while private char char8_t char16_t char32_t catch import module export virtual operator sizeof ' +
'dynamic_cast|10 typedef const_cast|10 const for static_cast|10 union namespace ' +
'unsigned long volatile static protected bool template mutable if public friend ' +
'do goto auto void enum else break extern using asm case typeid wchar_t ' +
'short reinterpret_cast|10 default double register explicit signed typename try this ' +
'switch continue inline delete alignas alignof constexpr consteval constinit decltype ' +
'concept co_await co_return co_yield requires ' +
'noexcept static_assert thread_local restrict final override ' +
'atomic_bool atomic_char atomic_schar ' +
'atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong ' +
'atomic_ullong new throw return ' +
'and and_eq bitand bitor compl not not_eq or or_eq xor xor_eq',
built_in: 'std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream ' +
'auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set ' +
'unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos ' +
'asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp ' +
'fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper ' +
'isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow ' +
'printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp ' +
'strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan ' +
'vfprintf vprintf vsprintf endl initializer_list unique_ptr _Bool complex _Complex imaginary _Imaginary',
literal: 'true false nullptr NULL'
};
const EXPRESSION_CONTAINS = [
PREPROCESSOR,
CPP_PRIMITIVE_TYPES,
C_LINE_COMMENT_MODE,
hljs.C_BLOCK_COMMENT_MODE,
NUMBERS,
STRINGS
];
const EXPRESSION_CONTEXT = {
// This mode covers expression context where we can't expect a function
// definition and shouldn't highlight anything that looks like one:
// `return some()`, `else if()`, `(x*sum(1, 2))`
variants: [{
begin: /=/,
end: /;/
},
{
begin: /\(/,
end: /\)/
},
{
beginKeywords: 'new throw return else',
end: /;/
}
],
keywords: CPP_KEYWORDS,
contains: EXPRESSION_CONTAINS.concat([{
begin: /\(/,
end: /\)/,
keywords: CPP_KEYWORDS,
contains: EXPRESSION_CONTAINS.concat(['self']),
relevance: 0
}]),
relevance: 0
};
const FUNCTION_DECLARATION = {
className: 'function',
begin: '(' + FUNCTION_TYPE_RE + '[\\*&\\s]+)+' + FUNCTION_TITLE,
returnBegin: true,
end: /[{;=]/,
excludeEnd: true,
keywords: CPP_KEYWORDS,
illegal: /[^\w\s\*&:<>]/,
contains: [{ // to prevent it from being confused as the function title
begin: DECLTYPE_AUTO_RE,
keywords: CPP_KEYWORDS,
relevance: 0
},
{
begin: FUNCTION_TITLE,
returnBegin: true,
contains: [TITLE_MODE],
relevance: 0
},
{
className: 'params',
begin: /\(/,
end: /\)/,
keywords: CPP_KEYWORDS,
relevance: 0,
contains: [
C_LINE_COMMENT_MODE,
hljs.C_BLOCK_COMMENT_MODE,
STRINGS,
NUMBERS,
CPP_PRIMITIVE_TYPES,
// Count matching parentheses.
{
begin: /\(/,
end: /\)/,
keywords: CPP_KEYWORDS,
relevance: 0,
contains: [
'self',
C_LINE_COMMENT_MODE,
hljs.C_BLOCK_COMMENT_MODE,
STRINGS,
NUMBERS,
CPP_PRIMITIVE_TYPES
]
}
]
},
CPP_PRIMITIVE_TYPES,
C_LINE_COMMENT_MODE,
hljs.C_BLOCK_COMMENT_MODE,
PREPROCESSOR
]
};
return {
aliases: [
'c',
'cc',
'h',
'c++',
'h++',
'hpp',
'hh',
'hxx',
'cxx'
],
keywords: CPP_KEYWORDS,
// the base c-like language will NEVER be auto-detected, rather the
// derivitives: c, c++, arduino turn auto-detect back on for themselves
disableAutodetect: true,
illegal: '</',
contains: [].concat(
EXPRESSION_CONTEXT,
FUNCTION_DECLARATION,
EXPRESSION_CONTAINS,
[
PREPROCESSOR,
{ // containers: ie, `vector <int> rooms (9);`
begin: '\\b(deque|list|queue|priority_queue|pair|stack|vector|map|set|bitset|multiset|multimap|unordered_map|unordered_set|unordered_multiset|unordered_multimap|array)\\s*<',
end: '>',
keywords: CPP_KEYWORDS,
contains: [
'self',
CPP_PRIMITIVE_TYPES
]
},
{
begin: hljs.IDENT_RE + '::',
keywords: CPP_KEYWORDS
},
{
className: 'class',
beginKeywords: 'enum class struct union',
end: /[{;:<>=]/,
contains: [{
beginKeywords: "final class struct"
},
hljs.TITLE_MODE
]
}
]),
exports: {
preprocessor: PREPROCESSOR,
strings: STRINGS,
keywords: CPP_KEYWORDS
}
};
}
@@ -0,0 +1,26 @@
// 本文件由FirstUI授权予佛山市航电梦联网络科技有限公司(会员ID:1 127,营业执照号:91 4 4 06 0 5MA55 6 H 1 KXH)专用,请尊重知识产权,勿私下传播,违者追究法律责任。
/*
Language: C
Category: common, system
Website: https://en.wikipedia.org/wiki/C_(programming_language)
Modified by: firstui
organization: FirstUI(https://www.firstui.cn/)
*/
import cLike from './c-like.js';
/** @type LanguageFn */
export default function(hljs) {
const lang = cLike(hljs);
// Until C is actually different than C++ there is no reason to auto-detect C
// as it's own language since it would just fail auto-detect testing or
// simply match with C++.
//
// See further comments in c-like.js.
// lang.disableAutodetect = false;
lang.name = 'C';
lang.aliases = ['c', 'h'];
return lang;
}
@@ -0,0 +1,21 @@
// 本文件由FirstUI授权予佛山市航电梦联网络科技有限公司(会员ID:11 2 7,营业执照号: 9 1 440 6 0 5MA 5 56H1 KXH)专用,请尊重知识产权,勿私下传播,违者追究法律责任。
/*
Language: C++
Category: common, system
Website: https://isocpp.org
Modified by: firstui
organization: FirstUI(https://www.firstui.cn/)
*/
import cLike from './c-like.js';
/** @type LanguageFn */
export default function(hljs) {
const lang = cLike(hljs);
// return auto-detection back on
lang.disableAutodetect = false;
lang.name = 'C++';
lang.aliases = ['cc', 'c++', 'h++', 'hpp', 'hh', 'hxx', 'cxx'];
return lang;
}
@@ -0,0 +1,130 @@
// 本文件由FirstUI授权予佛山市航电梦联网络科技有限公司(会员ID:1 1 27,营业执照号:91 4 40 6 0 5 MA5 56 H 1K XH)专用,请尊重知识产权,勿私下传播,违者追究法律责任。
/**
* Modified by: firstui
* organization: FirstUI(https://www.firstui.cn/)
*/
export default function(hljs) {
var FUNCTION_LIKE = {
begin: /[\w-]+\(/, returnBegin: true,
contains: [
{
className: 'built_in',
begin: /[\w-]+/
},
{
begin: /\(/, end: /\)/,
contains: [
hljs.APOS_STRING_MODE,
hljs.QUOTE_STRING_MODE,
hljs.CSS_NUMBER_MODE,
]
}
]
}
var ATTRIBUTE = {
className: 'attribute',
begin: /\S/, end: ':', excludeEnd: true,
starts: {
endsWithParent: true, excludeEnd: true,
contains: [
FUNCTION_LIKE,
hljs.CSS_NUMBER_MODE,
hljs.QUOTE_STRING_MODE,
hljs.APOS_STRING_MODE,
hljs.C_BLOCK_COMMENT_MODE,
{
className: 'number', begin: '#[0-9A-Fa-f]+'
},
{
className: 'meta', begin: '!important'
}
]
}
}
var AT_IDENTIFIER = '@[a-z-]+' // @font-face
var AT_MODIFIERS = "and or not only"
var MEDIA_TYPES = "all print screen speech"
var AT_PROPERTY_RE = /@\-?\w[\w]*(\-\w+)*/ // @-webkit-keyframes
var IDENT_RE = '[a-zA-Z-][a-zA-Z0-9_-]*';
var RULE = {
begin: /(?:[A-Z\_\.\-]+|--[a-zA-Z0-9_-]+)\s*:/, returnBegin: true, end: ';', endsWithParent: true,
contains: [
ATTRIBUTE
]
};
return {
case_insensitive: true,
illegal: /[=\/|'\$]/,
contains: [
hljs.C_BLOCK_COMMENT_MODE,
{
className: 'selector-id', begin: /#[A-Za-z0-9_-]+/
},
{
className: 'selector-class', begin: /\.[A-Za-z0-9_-]+/
},
{
className: 'selector-attr',
begin: /\[/, end: /\]/,
illegal: '$',
contains: [
hljs.APOS_STRING_MODE,
hljs.QUOTE_STRING_MODE,
]
},
{
className: 'selector-pseudo',
begin: /:(:)?[a-zA-Z0-9\_\-\+\(\)"'.]+/
},
// matching these here allows us to treat them more like regular CSS
// rules so everything between the {} gets regular rule highlighting,
// which is what we want for page and font-face
{
begin: '@(page|font-face)',
lexemes: AT_IDENTIFIER,
keywords: '@page @font-face'
},
{
begin: '@', end: '[{;]', // at_rule eating first "{" is a good thing
// because it doesnt let it to be parsed as
// a rule set but instead drops parser into
// the default mode which is how it should be.
illegal: /:/, // break on Less variables @var: ...
returnBegin: true,
contains: [
{
className: 'keyword',
begin: AT_PROPERTY_RE
},
{
begin: /\s/, endsWithParent: true, excludeEnd: true,
relevance: 0,
keywords: AT_MODIFIERS,
contains: [
{
begin: /[a-z-]+:/,
className:"attribute"
},
hljs.APOS_STRING_MODE,
hljs.QUOTE_STRING_MODE,
hljs.CSS_NUMBER_MODE
]
}
]
},
{
className: 'selector-tag', begin: IDENT_RE,
relevance: 0
},
{
begin: '{', end: '}',
illegal: /\S/,
contains: [
hljs.C_BLOCK_COMMENT_MODE,
RULE,
]
}
]
};
};
@@ -0,0 +1,246 @@
// 本文件由FirstUI授权予佛山市航电梦联网络科技有限公司(会员ID: 1127,营业执照号: 9 14406 0 5MA 5 5 6 H1K X H)专用,请尊重知识产权,勿私下传播,违者追究法律责任。
/**
* Modified by: firstui
* organization: FirstUI(https://www.firstui.cn/)
*/
export default function(hljs) {
var FRAGMENT = {
begin: '<>',
end: '</>'
};
var XML_TAG = {
begin: /<[A-Za-z0-9\\._:-]+/,
end: /\/[A-Za-z0-9\\._:-]+>|\/>/
};
var IDENT_RE = '[A-Za-z$_][0-9A-Za-z$_]*';
var KEYWORDS = {
keyword:
'in of if for while finally var new function do return void else break catch ' +
'instanceof with throw case default try this switch continue typeof delete ' +
'let yield const export super debugger as async await static ' +
// ECMAScript 6 modules import
'import from as'
,
literal:
'true false null undefined NaN Infinity',
built_in:
'eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent ' +
'encodeURI encodeURIComponent escape unescape Object Function Boolean Error ' +
'EvalError InternalError RangeError ReferenceError StopIteration SyntaxError ' +
'TypeError URIError Number Math Date String RegExp Array Float32Array ' +
'Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array ' +
'Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require ' +
'module console window document Symbol Set Map WeakSet WeakMap Proxy Reflect ' +
'Promise'
};
var NUMBER = {
className: 'number',
variants: [
{ begin: '\\b(0[bB][01]+)n?' },
{ begin: '\\b(0[oO][0-7]+)n?' },
{ begin: hljs.C_NUMBER_RE + 'n?' }
],
relevance: 0
};
var SUBST = {
className: 'subst',
begin: '\\$\\{', end: '\\}',
keywords: KEYWORDS,
contains: [] // defined later
};
var HTML_TEMPLATE = {
begin: 'html`', end: '',
starts: {
end: '`', returnEnd: false,
contains: [
hljs.BACKSLASH_ESCAPE,
SUBST
],
subLanguage: 'xml',
}
};
var CSS_TEMPLATE = {
begin: 'css`', end: '',
starts: {
end: '`', returnEnd: false,
contains: [
hljs.BACKSLASH_ESCAPE,
SUBST
],
subLanguage: 'css',
}
};
var TEMPLATE_STRING = {
className: 'string',
begin: '`', end: '`',
contains: [
hljs.BACKSLASH_ESCAPE,
SUBST
]
};
SUBST.contains = [
hljs.APOS_STRING_MODE,
hljs.QUOTE_STRING_MODE,
HTML_TEMPLATE,
CSS_TEMPLATE,
TEMPLATE_STRING,
NUMBER,
hljs.REGEXP_MODE
];
var PARAMS_CONTAINS = SUBST.contains.concat([
hljs.C_BLOCK_COMMENT_MODE,
hljs.C_LINE_COMMENT_MODE
]);
return {
aliases: ['js', 'jsx', 'mjs', 'cjs'],
keywords: KEYWORDS,
contains: [
{
className: 'meta',
relevance: 10,
begin: /^\s*['"]use (strict|asm)['"]/
},
{
className: 'meta',
begin: /^#!/, end: /$/
},
hljs.APOS_STRING_MODE,
hljs.QUOTE_STRING_MODE,
HTML_TEMPLATE,
CSS_TEMPLATE,
TEMPLATE_STRING,
hljs.C_LINE_COMMENT_MODE,
hljs.COMMENT(
'/\\*\\*',
'\\*/',
{
relevance : 0,
contains : [
{
className : 'doctag',
begin : '@[A-Za-z]+',
contains : [
{
className: 'type',
begin: '\\{',
end: '\\}',
relevance: 0
},
{
className: 'variable',
begin: IDENT_RE + '(?=\\s*(-)|$)',
endsParent: true,
relevance: 0
},
// eat spaces (not newlines) so we can find
// types or variables
{
begin: /(?=[^\n])\s/,
relevance: 0
},
]
}
]
}
),
hljs.C_BLOCK_COMMENT_MODE,
NUMBER,
{ // object attr container
begin: /[{,\n]\s*/, relevance: 0,
contains: [
{
begin: IDENT_RE + '\\s*:', returnBegin: true,
relevance: 0,
contains: [{className: 'attr', begin: IDENT_RE, relevance: 0}]
}
]
},
{ // "value" container
begin: '(' + hljs.RE_STARTERS_RE + '|\\b(case|return|throw)\\b)\\s*',
keywords: 'return throw case',
contains: [
hljs.C_LINE_COMMENT_MODE,
hljs.C_BLOCK_COMMENT_MODE,
hljs.REGEXP_MODE,
{
className: 'function',
begin: '(\\(.*?\\)|' + IDENT_RE + ')\\s*=>', returnBegin: true,
end: '\\s*=>',
contains: [
{
className: 'params',
variants: [
{
begin: IDENT_RE
},
{
begin: /\(\s*\)/,
},
{
begin: /\(/, end: /\)/,
excludeBegin: true, excludeEnd: true,
keywords: KEYWORDS,
contains: PARAMS_CONTAINS
}
]
}
]
},
{
className: '',
begin: /\s/,
end: /\s*/,
skip: true,
},
{ // JSX
variants: [
{ begin: FRAGMENT.begin, end: FRAGMENT.end },
{ begin: XML_TAG.begin, end: XML_TAG.end }
],
subLanguage: 'xml',
contains: [
{
begin: XML_TAG.begin, end: XML_TAG.end, skip: true,
contains: ['self']
}
]
},
],
relevance: 0
},
{
className: 'function',
beginKeywords: 'function', end: /\{/, excludeEnd: true,
contains: [
hljs.inherit(hljs.TITLE_MODE, {begin: IDENT_RE}),
{
className: 'params',
begin: /\(/, end: /\)/,
excludeBegin: true,
excludeEnd: true,
contains: PARAMS_CONTAINS
}
],
illegal: /\[|%/
},
{
begin: /\$[(.]/ // relevance booster for a pattern common to JS libs: `$(something)` and `$.something`
},
hljs.METHOD_GUARD,
{ // ES6 class
className: 'class',
beginKeywords: 'class', end: /[{;=]/, excludeEnd: true,
illegal: /[:"\[\]]/,
contains: [
{beginKeywords: 'extends'},
hljs.UNDERSCORE_TITLE_MODE
]
},
{
beginKeywords: 'constructor get set', end: /\{/, excludeEnd: true
}
],
illegal: /#(?!!)/
};
};
@@ -0,0 +1,113 @@
// 本文件由FirstUI授权予佛山市航电梦联网络科技有限公司(会员ID: 1 127,营业执照号: 91 4 4 0 605 M A 556 H1 KXH)专用,请尊重知识产权,勿私下传播,违者追究法律责任。
/**
* Modified by: firstui
* organization: FirstUI(https://www.firstui.cn/)
*/
export default function(hljs) {
return {
aliases: ['md', 'mkdown', 'mkd'],
contains: [
// highlight headers
{
className: 'section',
variants: [
{ begin: '^#{1,6}', end: '$' },
{ begin: '^.+?\\n[=-]{2,}$' }
]
},
// inline html
{
begin: '<', end: '>',
subLanguage: 'xml',
relevance: 0
},
// lists (indicators only)
{
className: 'bullet',
begin: '^\\s*([*+-]|(\\d+\\.))\\s+'
},
// strong segments
{
className: 'strong',
begin: '[*_]{2}.+?[*_]{2}'
},
// emphasis segments
{
className: 'emphasis',
variants: [
{ begin: '\\*.+?\\*' },
{ begin: '_.+?_'
, relevance: 0
}
]
},
// blockquotes
{
className: 'quote',
begin: '^>\\s+', end: '$'
},
// code snippets
{
className: 'code',
variants: [
{
begin: '^```\\w*\\s*$', end: '^```[ ]*$'
},
{
begin: '`.+?`'
},
{
begin: '^( {4}|\\t)', end: '$',
relevance: 0
}
]
},
// horizontal rules
{
begin: '^[-\\*]{3,}', end: '$'
},
// using links - title and link
{
begin: '\\[.+?\\][\\(\\[].*?[\\)\\]]',
returnBegin: true,
contains: [
{
className: 'string',
begin: '\\[', end: '\\]',
excludeBegin: true,
returnEnd: true,
relevance: 0
},
{
className: 'link',
begin: '\\]\\(', end: '\\)',
excludeBegin: true, excludeEnd: true
},
{
className: 'symbol',
begin: '\\]\\[', end: '\\]',
excludeBegin: true, excludeEnd: true
}
],
relevance: 10
},
{
begin: /^\[[^\n]+\]:/,
returnBegin: true,
contains: [
{
className: 'symbol',
begin: /\[/, end: /\]/,
excludeBegin: true, excludeEnd: true
},
{
className: 'link',
begin: /:\s*/, end: /$/,
excludeBegin: true
}
]
}
]
};
};
@@ -0,0 +1,165 @@
// 本文件由FirstUI授权予佛山市航电梦联网络科技有限公司(会员ID:11 2 7,营业执照号: 9144060 5 MA 556 H1 K X H)专用,请尊重知识产权,勿私下传播,违者追究法律责任。
/**
* Modified by: firstui
* organization: FirstUI(https://www.firstui.cn/)
*/
export default function(hljs) {
var COMMENT_MODE = hljs.COMMENT('--', '$');
return {
case_insensitive: true,
illegal: /[<>{}*]/,
contains: [
{
beginKeywords:
'begin end start commit rollback savepoint lock alter create drop rename call ' +
'delete do handler insert load replace select truncate update set show pragma grant ' +
'merge describe use explain help declare prepare execute deallocate release ' +
'unlock purge reset change stop analyze cache flush optimize repair kill ' +
'install uninstall checksum restore check backup revoke comment values with',
end: /;/, endsWithParent: true,
lexemes: /[\w\.]+/,
keywords: {
keyword:
'as abort abs absolute acc acce accep accept access accessed accessible account acos action activate add ' +
'addtime admin administer advanced advise aes_decrypt aes_encrypt after agent aggregate ali alia alias ' +
'all allocate allow alter always analyze ancillary and anti any anydata anydataset anyschema anytype apply ' +
'archive archived archivelog are as asc ascii asin assembly assertion associate asynchronous at atan ' +
'atn2 attr attri attrib attribu attribut attribute attributes audit authenticated authentication authid ' +
'authors auto autoallocate autodblink autoextend automatic availability avg backup badfile basicfile ' +
'before begin beginning benchmark between bfile bfile_base big bigfile bin binary_double binary_float ' +
'binlog bit_and bit_count bit_length bit_or bit_xor bitmap blob_base block blocksize body both bound ' +
'bucket buffer_cache buffer_pool build bulk by byte byteordermark bytes cache caching call calling cancel ' +
'capacity cascade cascaded case cast catalog category ceil ceiling chain change changed char_base ' +
'char_length character_length characters characterset charindex charset charsetform charsetid check ' +
'checksum checksum_agg child choose chr chunk class cleanup clear client clob clob_base clone close ' +
'cluster_id cluster_probability cluster_set clustering coalesce coercibility col collate collation ' +
'collect colu colum column column_value columns columns_updated comment commit compact compatibility ' +
'compiled complete composite_limit compound compress compute concat concat_ws concurrent confirm conn ' +
'connec connect connect_by_iscycle connect_by_isleaf connect_by_root connect_time connection ' +
'consider consistent constant constraint constraints constructor container content contents context ' +
'contributors controlfile conv convert convert_tz corr corr_k corr_s corresponding corruption cos cost ' +
'count count_big counted covar_pop covar_samp cpu_per_call cpu_per_session crc32 create creation ' +
'critical cross cube cume_dist curdate current current_date current_time current_timestamp current_user ' +
'cursor curtime customdatum cycle data database databases datafile datafiles datalength date_add ' +
'date_cache date_format date_sub dateadd datediff datefromparts datename datepart datetime2fromparts ' +
'day day_to_second dayname dayofmonth dayofweek dayofyear days db_role_change dbtimezone ddl deallocate ' +
'declare decode decompose decrement decrypt deduplicate def defa defau defaul default defaults ' +
'deferred defi defin define degrees delayed delegate delete delete_all delimited demand dense_rank ' +
'depth dequeue des_decrypt des_encrypt des_key_file desc descr descri describ describe descriptor ' +
'deterministic diagnostics difference dimension direct_load directory disable disable_all ' +
'disallow disassociate discardfile disconnect diskgroup distinct distinctrow distribute distributed div ' +
'do document domain dotnet double downgrade drop dumpfile duplicate duration each edition editionable ' +
'editions element ellipsis else elsif elt empty enable enable_all enclosed encode encoding encrypt ' +
'end end-exec endian enforced engine engines enqueue enterprise entityescaping eomonth error errors ' +
'escaped evalname evaluate event eventdata events except exception exceptions exchange exclude excluding ' +
'execu execut execute exempt exists exit exp expire explain explode export export_set extended extent external ' +
'external_1 external_2 externally extract failed failed_login_attempts failover failure far fast ' +
'feature_set feature_value fetch field fields file file_name_convert filesystem_like_logging final ' +
'finish first first_value fixed flash_cache flashback floor flush following follows for forall force foreign ' +
'form forma format found found_rows freelist freelists freepools fresh from from_base64 from_days ' +
'ftp full function general generated get get_format get_lock getdate getutcdate global global_name ' +
'globally go goto grant grants greatest group group_concat group_id grouping grouping_id groups ' +
'gtid_subtract guarantee guard handler hash hashkeys having hea head headi headin heading heap help hex ' +
'hierarchy high high_priority hosts hour hours http id ident_current ident_incr ident_seed identified ' +
'identity idle_time if ifnull ignore iif ilike ilm immediate import in include including increment ' +
'index indexes indexing indextype indicator indices inet6_aton inet6_ntoa inet_aton inet_ntoa infile ' +
'initial initialized initially initrans inmemory inner innodb input insert install instance instantiable ' +
'instr interface interleaved intersect into invalidate invisible is is_free_lock is_ipv4 is_ipv4_compat ' +
'is_not is_not_null is_used_lock isdate isnull isolation iterate java join json json_exists ' +
'keep keep_duplicates key keys kill language large last last_day last_insert_id last_value lateral lax lcase ' +
'lead leading least leaves left len lenght length less level levels library like like2 like4 likec limit ' +
'lines link list listagg little ln load load_file lob lobs local localtime localtimestamp locate ' +
'locator lock locked log log10 log2 logfile logfiles logging logical logical_reads_per_call ' +
'logoff logon logs long loop low low_priority lower lpad lrtrim ltrim main make_set makedate maketime ' +
'managed management manual map mapping mask master master_pos_wait match matched materialized max ' +
'maxextents maximize maxinstances maxlen maxlogfiles maxloghistory maxlogmembers maxsize maxtrans ' +
'md5 measures median medium member memcompress memory merge microsecond mid migration min minextents ' +
'minimum mining minus minute minutes minvalue missing mod mode model modification modify module monitoring month ' +
'months mount move movement multiset mutex name name_const names nan national native natural nav nchar ' +
'nclob nested never new newline next nextval no no_write_to_binlog noarchivelog noaudit nobadfile ' +
'nocheck nocompress nocopy nocycle nodelay nodiscardfile noentityescaping noguarantee nokeep nologfile ' +
'nomapping nomaxvalue nominimize nominvalue nomonitoring none noneditionable nonschema noorder ' +
'nopr nopro noprom nopromp noprompt norely noresetlogs noreverse normal norowdependencies noschemacheck ' +
'noswitch not nothing notice notnull notrim novalidate now nowait nth_value nullif nulls num numb numbe ' +
'nvarchar nvarchar2 object ocicoll ocidate ocidatetime ociduration ociinterval ociloblocator ocinumber ' +
'ociref ocirefcursor ocirowid ocistring ocitype oct octet_length of off offline offset oid oidindex old ' +
'on online only opaque open operations operator optimal optimize option optionally or oracle oracle_date ' +
'oradata ord ordaudio orddicom orddoc order ordimage ordinality ordvideo organization orlany orlvary ' +
'out outer outfile outline output over overflow overriding package pad parallel parallel_enable ' +
'parameters parent parse partial partition partitions pascal passing password password_grace_time ' +
'password_lock_time password_reuse_max password_reuse_time password_verify_function patch path patindex ' +
'pctincrease pctthreshold pctused pctversion percent percent_rank percentile_cont percentile_disc ' +
'performance period period_add period_diff permanent physical pi pipe pipelined pivot pluggable plugin ' +
'policy position post_transaction pow power pragma prebuilt precedes preceding precision prediction ' +
'prediction_cost prediction_details prediction_probability prediction_set prepare present preserve ' +
'prior priority private private_sga privileges procedural procedure procedure_analyze processlist ' +
'profiles project prompt protection public publishingservername purge quarter query quick quiesce quota ' +
'quotename radians raise rand range rank raw read reads readsize rebuild record records ' +
'recover recovery recursive recycle redo reduced ref reference referenced references referencing refresh ' +
'regexp_like register regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy ' +
'reject rekey relational relative relaylog release release_lock relies_on relocate rely rem remainder rename ' +
'repair repeat replace replicate replication required reset resetlogs resize resource respect restore ' +
'restricted result result_cache resumable resume retention return returning returns reuse reverse revoke ' +
'right rlike role roles rollback rolling rollup round row row_count rowdependencies rowid rownum rows ' +
'rtrim rules safe salt sample save savepoint sb1 sb2 sb4 scan schema schemacheck scn scope scroll ' +
'sdo_georaster sdo_topo_geometry search sec_to_time second seconds section securefile security seed segment select ' +
'self semi sequence sequential serializable server servererror session session_user sessions_per_user set ' +
'sets settings sha sha1 sha2 share shared shared_pool short show shrink shutdown si_averagecolor ' +
'si_colorhistogram si_featurelist si_positionalcolor si_stillimage si_texture siblings sid sign sin ' +
'size size_t sizes skip slave sleep smalldatetimefromparts smallfile snapshot some soname sort soundex ' +
'source space sparse spfile split sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows ' +
'sql_small_result sql_variant_property sqlcode sqldata sqlerror sqlname sqlstate sqrt square standalone ' +
'standby start starting startup statement static statistics stats_binomial_test stats_crosstab ' +
'stats_ks_test stats_mode stats_mw_test stats_one_way_anova stats_t_test_ stats_t_test_indep ' +
'stats_t_test_one stats_t_test_paired stats_wsr_test status std stddev stddev_pop stddev_samp stdev ' +
'stop storage store stored str str_to_date straight_join strcmp strict string struct stuff style subdate ' +
'subpartition subpartitions substitutable substr substring subtime subtring_index subtype success sum ' +
'suspend switch switchoffset switchover sync synchronous synonym sys sys_xmlagg sysasm sysaux sysdate ' +
'sysdatetimeoffset sysdba sysoper system system_user sysutcdatetime table tables tablespace tablesample tan tdo ' +
'template temporary terminated tertiary_weights test than then thread through tier ties time time_format ' +
'time_zone timediff timefromparts timeout timestamp timestampadd timestampdiff timezone_abbr ' +
'timezone_minute timezone_region to to_base64 to_date to_days to_seconds todatetimeoffset trace tracking ' +
'transaction transactional translate translation treat trigger trigger_nestlevel triggers trim truncate ' +
'try_cast try_convert try_parse type ub1 ub2 ub4 ucase unarchived unbounded uncompress ' +
'under undo unhex unicode uniform uninstall union unique unix_timestamp unknown unlimited unlock unnest unpivot ' +
'unrecoverable unsafe unsigned until untrusted unusable unused update updated upgrade upped upper upsert ' +
'url urowid usable usage use use_stored_outlines user user_data user_resources users using utc_date ' +
'utc_timestamp uuid uuid_short validate validate_password_strength validation valist value values var ' +
'var_samp varcharc vari varia variab variabl variable variables variance varp varraw varrawc varray ' +
'verify version versions view virtual visible void wait wallet warning warnings week weekday weekofyear ' +
'wellformed when whene whenev wheneve whenever where while whitespace window with within without work wrapped ' +
'xdb xml xmlagg xmlattributes xmlcast xmlcolattval xmlelement xmlexists xmlforest xmlindex xmlnamespaces ' +
'xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltype xor year year_to_month years yearweek',
literal:
'true false null unknown',
built_in:
'array bigint binary bit blob bool boolean char character date dec decimal float int int8 integer interval number ' +
'numeric real record serial serial8 smallint text time timestamp tinyint varchar varchar2 varying void'
},
contains: [
{
className: 'string',
begin: '\'', end: '\'',
contains: [{begin: '\'\''}]
},
{
className: 'string',
begin: '"', end: '"',
contains: [{begin: '""'}]
},
{
className: 'string',
begin: '`', end: '`'
},
hljs.C_NUMBER_MODE,
hljs.C_BLOCK_COMMENT_MODE,
COMMENT_MODE,
hljs.HASH_COMMENT_MODE
]
},
hljs.C_BLOCK_COMMENT_MODE,
COMMENT_MODE,
hljs.HASH_COMMENT_MODE
]
};
};
@@ -0,0 +1,210 @@
// 本文件由FirstUI授权予佛山市航电梦联网络科技有限公司(会员ID:1 127,营业执照号:9 1440 60 5MA5 5 6 H 1 KX H)专用,请尊重知识产权,勿私下传播,违者追究法律责任。
/**
* Modified by: firstui
* organization: FirstUI(https://www.firstui.cn/)
*/
export default function(hljs) {
var JS_IDENT_RE = '[A-Za-z$_][0-9A-Za-z$_]*';
var KEYWORDS = {
keyword:
'in if for while finally var new function do return void else break catch ' +
'instanceof with throw case default try this switch continue typeof delete ' +
'let yield const class public private protected get set super ' +
'static implements enum export import declare type namespace abstract ' +
'as from extends async await',
literal:
'true false null undefined NaN Infinity',
built_in:
'eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent ' +
'encodeURI encodeURIComponent escape unescape Object Function Boolean Error ' +
'EvalError InternalError RangeError ReferenceError StopIteration SyntaxError ' +
'TypeError URIError Number Math Date String RegExp Array Float32Array ' +
'Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array ' +
'Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require ' +
'module console window document any number boolean string void Promise'
};
var DECORATOR = {
className: 'meta',
begin: '@' + JS_IDENT_RE,
};
var ARGS =
{
begin: '\\(',
end: /\)/,
keywords: KEYWORDS,
contains: [
'self',
hljs.QUOTE_STRING_MODE,
hljs.APOS_STRING_MODE,
hljs.NUMBER_MODE
]
};
var PARAMS = {
className: 'params',
begin: /\(/, end: /\)/,
excludeBegin: true,
excludeEnd: true,
keywords: KEYWORDS,
contains: [
hljs.C_LINE_COMMENT_MODE,
hljs.C_BLOCK_COMMENT_MODE,
DECORATOR,
ARGS
]
};
var NUMBER = {
className: 'number',
variants: [
{ begin: '\\b(0[bB][01]+)n?' },
{ begin: '\\b(0[oO][0-7]+)n?' },
{ begin: hljs.C_NUMBER_RE + 'n?' }
],
relevance: 0
};
var SUBST = {
className: 'subst',
begin: '\\$\\{', end: '\\}',
keywords: KEYWORDS,
contains: [] // defined later
};
var HTML_TEMPLATE = {
begin: 'html`', end: '',
starts: {
end: '`', returnEnd: false,
contains: [
hljs.BACKSLASH_ESCAPE,
SUBST
],
subLanguage: 'xml',
}
};
var CSS_TEMPLATE = {
begin: 'css`', end: '',
starts: {
end: '`', returnEnd: false,
contains: [
hljs.BACKSLASH_ESCAPE,
SUBST
],
subLanguage: 'css',
}
};
var TEMPLATE_STRING = {
className: 'string',
begin: '`', end: '`',
contains: [
hljs.BACKSLASH_ESCAPE,
SUBST
]
};
SUBST.contains = [
hljs.APOS_STRING_MODE,
hljs.QUOTE_STRING_MODE,
HTML_TEMPLATE,
CSS_TEMPLATE,
TEMPLATE_STRING,
NUMBER,
hljs.REGEXP_MODE
];
return {
aliases: ['ts'],
keywords: KEYWORDS,
contains: [
{
className: 'meta',
begin: /^\s*['"]use strict['"]/
},
hljs.APOS_STRING_MODE,
hljs.QUOTE_STRING_MODE,
HTML_TEMPLATE,
CSS_TEMPLATE,
TEMPLATE_STRING,
hljs.C_LINE_COMMENT_MODE,
hljs.C_BLOCK_COMMENT_MODE,
NUMBER,
{ // "value" container
begin: '(' + hljs.RE_STARTERS_RE + '|\\b(case|return|throw)\\b)\\s*',
keywords: 'return throw case',
contains: [
hljs.C_LINE_COMMENT_MODE,
hljs.C_BLOCK_COMMENT_MODE,
hljs.REGEXP_MODE,
{
className: 'function',
begin: '(\\(.*?\\)|' + hljs.IDENT_RE + ')\\s*=>', returnBegin: true,
end: '\\s*=>',
contains: [
{
className: 'params',
variants: [
{
begin: hljs.IDENT_RE
},
{
begin: /\(\s*\)/,
},
{
begin: /\(/, end: /\)/,
excludeBegin: true, excludeEnd: true,
keywords: KEYWORDS,
contains: [
'self',
hljs.C_LINE_COMMENT_MODE,
hljs.C_BLOCK_COMMENT_MODE
]
}
]
}
]
}
],
relevance: 0
},
{
className: 'function',
beginKeywords: 'function', end: /[\{;]/, excludeEnd: true,
keywords: KEYWORDS,
contains: [
'self',
hljs.inherit(hljs.TITLE_MODE, { begin: JS_IDENT_RE }),
PARAMS
],
illegal: /%/,
relevance: 0 // () => {} is more typical in TypeScript
},
{
beginKeywords: 'constructor', end: /[\{;]/, excludeEnd: true,
contains: [
'self',
PARAMS
]
},
{ // prevent references like module.id from being higlighted as module definitions
begin: /module\./,
keywords: { built_in: 'module' },
relevance: 0
},
{
beginKeywords: 'module', end: /\{/, excludeEnd: true
},
{
beginKeywords: 'interface', end: /\{/, excludeEnd: true,
keywords: 'interface extends'
},
{
begin: /\$[(.]/ // relevance booster for a pattern common to JS libs: `$(something)` and `$.something`
},
{
begin: '\\.' + hljs.IDENT_RE, relevance: 0 // hack: prevents detection of keywords after dots
},
DECORATOR,
ARGS
]
};
};
@@ -0,0 +1,150 @@
// 本文件由FirstUI授权予佛山市航电梦联网络科技有限公司(会员ID: 1 1 27,营业执照号: 9 1 44 0 60 5 M A 556 H1 K XH)专用,请尊重知识产权,勿私下传播,违者追究法律责任。
/**
* Modified by: firstui
* organization: FirstUI(https://www.firstui.cn/)
*/
export default function(hljs) {
var XML_IDENT_RE = '[A-Za-z0-9\\._:-]+';
var XML_ENTITIES = {
className: 'symbol',
begin: '&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;'
};
var XML_META_KEYWORDS = {
begin: '\\s',
contains:[
{
className: 'meta-keyword',
begin: '#?[a-z_][a-z1-9_-]+',
illegal: '\\n',
}
]
};
var XML_META_PAR_KEYWORDS = hljs.inherit(XML_META_KEYWORDS, {begin: '\\(', end: '\\)'});
var APOS_META_STRING_MODE = hljs.inherit(hljs.APOS_STRING_MODE, {className: 'meta-string'});
var QUOTE_META_STRING_MODE = hljs.inherit(hljs.QUOTE_STRING_MODE, {className: 'meta-string'});
var TAG_INTERNALS = {
endsWithParent: true,
illegal: /</,
relevance: 0,
contains: [
{
className: 'attr',
begin: XML_IDENT_RE,
relevance: 0
},
{
begin: /=\s*/,
relevance: 0,
contains: [
{
className: 'string',
endsParent: true,
variants: [
{begin: /"/, end: /"/, contains: [XML_ENTITIES]},
{begin: /'/, end: /'/, contains: [XML_ENTITIES]},
{begin: /[^\s"'=<>`]+/}
]
}
]
}
]
};
return {
aliases: ['html', 'xhtml', 'rss', 'atom', 'xjb', 'xsd', 'xsl', 'plist', 'wsf', 'svg'],
case_insensitive: true,
contains: [
{
className: 'meta',
begin: '<![a-z]', end: '>',
relevance: 10,
contains: [
XML_META_KEYWORDS,
QUOTE_META_STRING_MODE,
APOS_META_STRING_MODE,
XML_META_PAR_KEYWORDS,
{
begin: '\\[', end: '\\]',
contains:[
{
className: 'meta',
begin: '<![a-z]', end: '>',
contains: [
XML_META_KEYWORDS,
XML_META_PAR_KEYWORDS,
QUOTE_META_STRING_MODE,
APOS_META_STRING_MODE
]
}
]
}
]
},
hljs.COMMENT(
'<!--',
'-->',
{
relevance: 10
}
),
{
begin: '<\\!\\[CDATA\\[', end: '\\]\\]>',
relevance: 10
},
XML_ENTITIES,
{
className: 'meta',
begin: /<\?xml/, end: /\?>/, relevance: 10
},
{
begin: /<\?(php)?/, end: /\?>/,
subLanguage: 'php',
contains: [
// We don't want the php closing tag ?> to close the PHP block when
// inside any of the following blocks:
{begin: '/\\*', end: '\\*/', skip: true},
{begin: 'b"', end: '"', skip: true},
{begin: 'b\'', end: '\'', skip: true},
hljs.inherit(hljs.APOS_STRING_MODE, {illegal: null, className: null, contains: null, skip: true}),
hljs.inherit(hljs.QUOTE_STRING_MODE, {illegal: null, className: null, contains: null, skip: true})
]
},
{
className: 'tag',
/*
The lookahead pattern (?=...) ensures that 'begin' only matches
'<style' as a single word, followed by a whitespace or an
ending braket. The '$' is needed for the lexeme to be recognized
by hljs.subMode() that tests lexemes outside the stream.
*/
begin: '<style(?=\\s|>)', end: '>',
keywords: {name: 'style'},
contains: [TAG_INTERNALS],
starts: {
end: '</style>', returnEnd: true,
subLanguage: ['css', 'xml']
}
},
{
className: 'tag',
// See the comment in the <style tag about the lookahead pattern
begin: '<script(?=\\s|>)', end: '>',
keywords: {name: 'script'},
contains: [TAG_INTERNALS],
starts: {
end: '\<\/script\>', returnEnd: true,
subLanguage: ['actionscript', 'javascript', 'handlebars', 'xml']
}
},
{
className: 'tag',
begin: '</?', end: '/?>',
contains: [
{
className: 'name', begin: /[^\/><\s]+/, relevance: 0
},
TAG_INTERNALS
]
}
]
};
};
@@ -0,0 +1,134 @@
// 本文件由FirstUI授权予佛山市航电梦联网络科技有限公司(会员ID:11 27,营业执照号: 9144 0 60 5MA5 56H 1K XH)专用,请尊重知识产权,勿私下传播,违者追究法律责任。
/**
* @param {string} value
* @returns {RegExp}
* */
export function escape(value) {
return new RegExp(value.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'), 'm');
}
/**
* @param {RegExp | string } re
* @returns {string}
*/
export function source(re) {
if (!re) return null;
if (typeof re === "string") return re;
return re.source;
}
/**
* @param {RegExp | string } re
* @returns {string}
*/
export function lookahead(re) {
return concat('(?=', re, ')');
}
/**
* @param {RegExp | string } re
* @returns {string}
*/
export function anyNumberOfTimes(re) {
return concat('(', re, ')*');
}
/**
* @param {RegExp | string } re
* @returns {string}
*/
export function optional(re) {
return concat('(', re, ')?');
}
/**
* @param {...(RegExp | string) } args
* @returns {string}
*/
export function concat(...args) {
const joined = args.map((x) => source(x)).join("");
return joined;
}
/**
* Any of the passed expresssions may match
*
* Creates a huge this | this | that | that match
* @param {(RegExp | string)[] } args
* @returns {string}
*/
export function either(...args) {
const joined = '(' + args.map((x) => source(x)).join("|") + ")";
return joined;
}
/**
* @param {RegExp} re
* @returns {number}
*/
export function countMatchGroups(re) {
return (new RegExp(re.toString() + '|')).exec('').length - 1;
}
/**
* Does lexeme start with a regular expression match at the beginning
* @param {RegExp} re
* @param {string} lexeme
*/
export function startsWith(re, lexeme) {
const match = re && re.exec(lexeme);
return match && match.index === 0;
}
// join logically computes regexps.join(separator), but fixes the
// backreferences so they continue to match.
// it also places each individual regular expression into it's own
// match group, keeping track of the sequencing of those match groups
// is currently an exercise for the caller. :-)
/**
* @param {(string | RegExp)[]} regexps
* @param {string} separator
* @returns {string}
*/
export function join(regexps, separator = "|") {
// backreferenceRe matches an open parenthesis or backreference. To avoid
// an incorrect parse, it additionally matches the following:
// - [...] elements, where the meaning of parentheses and escapes change
// - other escape sequences, so we do not misparse escape sequences as
// interesting elements
// - non-matching or lookahead parentheses, which do not capture. These
// follow the '(' with a '?'.
const backreferenceRe = /\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;
let numCaptures = 0;
let ret = '';
for (let i = 0; i < regexps.length; i++) {
numCaptures += 1;
const offset = numCaptures;
let re = source(regexps[i]);
if (i > 0) {
ret += separator;
}
ret += "(";
while (re.length > 0) {
const match = backreferenceRe.exec(re);
if (match == null) {
ret += re;
break;
}
ret += re.substring(0, match.index);
re = re.substring(match.index + match[0].length);
if (match[0][0] === '\\' && match[1]) {
// Adjust the backreference.
ret += '\\' + String(Number(match[1]) + offset);
} else {
ret += match[0];
if (match[0] === '(') {
numCaptures++;
}
}
}
ret += ")";
}
return ret;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,324 @@
// 本文件由FirstUI授权予佛山市航电梦联网络科技有限公司(会员ID: 11 2 7,营业执照号: 91 44 06 05MA 556H 1 KX H)专用,请尊重知识产权,勿私下传播,违者追究法律责任。
/**
* html2Json 改造来自: https://github.com/Jxck/html2json
*
* Modified by: firstui
* organization: FirstUI(https://www.firstui.cn/)
*/
import wxDiscode from './wxDiscode.js'
import HTMLParser from './htmlparser.js'
var __placeImgeUrlHttps = "https";
var __emojisReg = '';
var __emojisBaseSrc = '';
var __emojis = {};
// Empty Elements - HTML 5
var empty = makeMap(
"area,base,basefont,br,col,frame,hr,img,input,link,meta,param,embed,command,keygen,source,track,wbr");
// Block Elements - HTML 5
var block = makeMap(
"br,a,code,address,article,applet,aside,audio,blockquote,button,canvas,center,dd,del,dir,div,dl,dt,fieldset,figcaption,figure,footer,form,frameset,h1,h2,h3,h4,h5,h6,header,hgroup,hr,iframe,ins,isindex,li,map,menu,noframes,noscript,object,ol,output,p,pre,section,script,table,tbody,td,tfoot,th,thead,tr,ul,video"
);
// Inline Elements - HTML 5
var inline = makeMap(
"abbr,acronym,applet,b,basefont,bdo,big,button,cite,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var"
);
// Elements that you can, intentionally, leave open
// (and which close themselves)
var closeSelf = makeMap("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr");
// Attributes that have their values filled in disabled="disabled"
var fillAttrs = makeMap(
"checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected");
// Special Elements (can contain anything)
var special = makeMap("wxxxcode-style,script,style,view,scroll-view,block");
function makeMap(str) {
var obj = {},
items = str.split(",");
for (var i = 0; i < items.length; i++)
obj[items[i]] = true;
return obj;
}
function q(v) {
return '"' + v + '"';
}
function removeDOCTYPE(html) {
return html
.replace(/<\?xml.*\?>\n/, '')
.replace(/<.*!doctype.*\>\n/, '')
.replace(/<.*!DOCTYPE.*\>\n/, '');
}
function trimHtml(html) {
return html
// .replace(/\r?\n+/g, '')
// .replace(/\n+/g, '')
.replace(/<!--.*?-->/ig, '')
// .replace(/\/\*.*?\*\//ig, '')
.replace(/[ ]+</ig, '<')
}
/**
* 过滤掉小程序无法展示的标签
* @param {*} html
*/
function removeInvalidTags(html) {
return html
.replace(/\<head(.|\n)*<\/head\>/ig, '')
.replace(/\<title(.|\n)*<\/title\>/ig, '')
.replace(/\<script(.|\n)*<\/script\>/ig, '')
.replace(/\<meta(.|\n)*<\/meta\>/ig, '')
.replace(/\<style(.|\n)*<\/style\>/gm, '')
}
function html2json(html, bindName) {
//处理字符串
html = removeDOCTYPE(html);
html = removeInvalidTags(html)
html = trimHtml(html);
html = wxDiscode.strDiscode(html);
//生成node节点
var bufArray = [];
var results = {
node: bindName,
nodes: [],
images: [],
imageUrls: []
};
var index = 0;
HTMLParser(html, {
start: function(tag, attrs, unary, content) {
//debug(tag, attrs, unary);
// node for this element
var node = {
node: 'element',
tag: tag,
};
// 判断是否需要添加标签主体内容
content && (node['content'] = content)
if (bufArray.length === 0) {
node.index = index.toString()
index += 1
} else {
var parent = bufArray[0];
if (parent.nodes === undefined) {
parent.nodes = [];
}
node.index = parent.index + '.' + parent.nodes.length
}
if (block[tag]) {
node.tagType = "block";
} else if (inline[tag]) {
node.tagType = "inline";
} else if (closeSelf[tag]) {
node.tagType = "closeSelf";
}
if (attrs.length !== 0) {
node.attr = attrs.reduce(function(pre, attr) {
var name = attr.name;
var value = attr.value;
if (name == 'class') {
// console.dir(value);
// value = value.join("")
node.classStr = value;
}
// has multi attibutes
// make it array of attribute
if (name == 'style') {
// console.dir(value);
// value = value.join("")
node.styleStr = value;
}
if (value.match(/ /)) {
value = value.split(' ');
}
// if attr already exists
// merge it
if (pre[name]) {
if (Array.isArray(pre[name])) {
// already array, push to last
pre[name].push(value);
} else {
// single value, make it array
pre[name] = [pre[name], value];
}
} else {
// not exist, put it
pre[name] = value;
}
return pre;
}, {});
}
//对img添加额外数据
if (node.tag === 'img') {
node.imgIndex = results.images.length;
var imgUrl = node.attr.src;
if (imgUrl[0] == '') {
imgUrl.splice(0, 1);
}
imgUrl = wxDiscode.urlToHttpUrl(imgUrl, __placeImgeUrlHttps);
node.attr.src = imgUrl;
node.from = bindName;
results.images.push(node);
results.imageUrls.push(imgUrl);
}
// 处理font标签样式属性
if (node.tag === 'font') {
var fontSize = ['x-small', 'small', 'medium', 'large', 'x-large', 'xx-large',
'-webkit-xxx-large'
];
var styleAttrs = {
'color': 'color',
'face': 'font-family',
'size': 'font-size'
};
if (!node.attr.style) node.attr.style = [];
if (!node.styleStr) node.styleStr = '';
for (var key in styleAttrs) {
if (node.attr[key]) {
var value = key === 'size' ? fontSize[node.attr[key] - 1] : node.attr[key];
node.attr.style.push(styleAttrs[key]);
node.attr.style.push(value);
node.styleStr += styleAttrs[key] + ': ' + value + ';';
}
}
}
//临时记录source资源
if (node.tag === 'source') {
results.source = node.attr.src;
}
if (unary) {
// if this tag doesn't have end tag
// like <img src="hoge.png"/>
// add to parents
var parent = bufArray[0] || results;
if (parent.nodes === undefined) {
parent.nodes = [];
}
parent.nodes.push(node);
} else {
bufArray.unshift(node);
}
},
end: function(tag) {
//debug(tag);
// merge into parent tag
var node = bufArray.shift();
if (node.tag !== tag) console.error('invalid state: mismatch end tag');
//当有缓存source资源时于于video补上src资源
if (node.tag === 'video' && results.source) {
node.attr.src = results.source;
delete results.source;
}
if (bufArray.length === 0) {
results.nodes.push(node);
} else {
var parent = bufArray[0];
if (parent.nodes === undefined) {
parent.nodes = [];
}
parent.nodes.push(node);
}
},
chars: function(text) {
//debug(text);
var node = {
node: 'text',
text: text,
textArray: transEmojiStr(text)
};
if (bufArray.length === 0) {
node.index = index.toString()
index += 1
results.nodes.push(node);
} else {
var parent = bufArray[0];
if (parent.nodes === undefined) {
parent.nodes = [];
}
node.index = parent.index + '.' + parent.nodes.length
parent.nodes.push(node);
}
},
comment: function(text) {
//debug(text);
// var node = {
// node: 'comment',
// text: text,
// };
// var parent = bufArray[0];
// if (parent.nodes === undefined) {
// parent.nodes = [];
// }
// parent.nodes.push(node);
},
});
return results;
};
function transEmojiStr(str) {
// var eReg = new RegExp("["+__reg+' '+"]");
// str = str.replace(/\[([^\[\]]+)\]/g,':$1:')
var emojiObjs = [];
//如果正则表达式为空
if (__emojisReg.length == 0 || !__emojis) {
var emojiObj = {}
emojiObj.node = "text";
emojiObj.text = str;
array = [emojiObj];
return array;
}
//这个地方需要调整
str = str.replace(/\[([^\[\]]+)\]/g, ':$1:')
var eReg = new RegExp("[:]");
var array = str.split(eReg);
for (var i = 0; i < array.length; i++) {
var ele = array[i];
var emojiObj = {};
if (__emojis[ele]) {
emojiObj.node = "element";
emojiObj.tag = "emoji";
emojiObj.text = __emojis[ele];
emojiObj.baseSrc = __emojisBaseSrc;
} else {
emojiObj.node = "text";
emojiObj.text = ele;
}
emojiObjs.push(emojiObj);
}
return emojiObjs;
}
function emojisInit(reg = '', baseSrc = "/fuiParse/emojis/", emojis) {
__emojisReg = reg;
__emojisBaseSrc = baseSrc;
__emojis = emojis;
}
export default {
html2json: html2json,
emojisInit: emojisInit
};
@@ -0,0 +1,200 @@
// 本文件由FirstUI授权予佛山市航电梦联网络科技有限公司(会员ID: 1127,营业执照号: 9 1 440 6 0 5 MA556H1K XH)专用,请尊重知识产权,勿私下传播,违者追究法律责任。
/**
* htmlParser改造自: https://github.com/blowsie/Pure-JavaScript-HTML5-Parser
*
* Modified by: firstui
* organization: FirstUI(https://www.firstui.cn/)
*/
// Regular Expressions for parsing tags and attributes
var startTag = /^<([-A-Za-z0-9_]+)((?:\s+[a-zA-Z_:][-a-zA-Z0-9_:.]*(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/,
endTag = /^<\/([-A-Za-z0-9_]+)[^>]*>/,
attr = /([a-zA-Z_:][-a-zA-Z0-9_:.]*)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g;
var codeTag = /^<code.*?>([\s\S]*?)<\/code>/
// Empty Elements - HTML 5
var empty = makeMap("area,base,basefont,br,col,frame,hr,img,input,link,meta,param,embed,command,keygen,source,track,wbr");
// Block Elements - HTML 5
var block = makeMap("a,address,code,article,applet,aside,audio,blockquote,button,canvas,center,dd,del,dir,div,dl,dt,fieldset,figcaption,figure,footer,form,frameset,h1,h2,h3,h4,h5,h6,header,hgroup,hr,iframe,ins,isindex,li,map,menu,noframes,noscript,object,ol,output,p,pre,section,script,table,tbody,td,tfoot,th,thead,tr,ul,video");
// Inline Elements - HTML 5
var inline = makeMap("abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var");
// Elements that you can, intentionally, leave open
// (and which close themselves)
var closeSelf = makeMap("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr");
// Attributes that have their values filled in disabled="disabled"
var fillAttrs = makeMap("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected");
// Special Elements (can contain anything)
var special = makeMap("wxxxcode-style,script,style,view,scroll-view,block");
// 存储code标签的内容
var codeContent = '';
function HTMLParser(html, handler) {
var index, chars, match, stack = [], last = html;
stack.last = function () {
return this[this.length - 1];
};
while (html) {
chars = true;
// Make sure we're not in a script or style element
if (!stack.last() || !special[stack.last()]) {
// Comment
if (html.indexOf("<!--") == 0) {
index = html.indexOf("-->");
if (index >= 0) {
if (handler.comment)
handler.comment(html.substring(4, index));
html = html.substring(index + 3);
chars = false;
}
// end tag
} else if (html.indexOf("</") == 0) {
match = html.match(endTag);
if (match) {
html = html.substring(match[0].length);
match[0].replace(endTag, parseEndTag);
chars = false;
}
// start tag
} else if (html.indexOf("<") == 0) {
match = html.match(startTag);
if (match) {
var tagName = match[1]
// code标签需要过滤文本节点
if (tagName === 'code') {
var codeTagMatch = html.match(codeTag)
codeContent = codeTagMatch[1] || ''
}
html = html.substring(match[0].length);
match[0].replace(startTag, parseStartTag);
chars = false;
}
}
if (chars) {
index = html.indexOf("<");
var text = ''
while (index === 0) {
text += "<";
html = html.substring(1);
index = html.indexOf("<");
}
text += index < 0 ? html : html.substring(0, index);
html = index < 0 ? "" : html.substring(index);
if (handler.chars && text.trim() !== '') {
handler.chars(text);
}
}
} else {
html = html.replace(new RegExp("([\\s\\S]*?)<\/" + stack.last() + "[^>]*>"), function (all, text) {
text = text.replace(/<!--([\s\S]*?)-->|<!\[CDATA\[([\s\S]*?)]]>/g, "$1$2");
if (handler.chars && text.trim() !== '')
handler.chars(text);
return "";
});
parseEndTag("", stack.last());
}
if (html == last)
throw "Parse Error: " + html;
last = html;
}
// Clean up any remaining tags
parseEndTag();
function parseStartTag(tag, tagName, rest, unary) {
tagName = tagName.toLowerCase();
if (block[tagName]) {
while (stack.last() && inline[stack.last()]) {
parseEndTag("", stack.last());
}
}
if (closeSelf[tagName] && stack.last() == tagName) {
parseEndTag("", tagName);
}
unary = empty[tagName] || !!unary;
if (!unary)
stack.push(tagName);
if (handler.start) {
var attrs = [];
rest.replace(attr, function (match, name) {
var value = arguments[2] ? arguments[2] :
arguments[3] ? arguments[3] :
arguments[4] ? arguments[4] :
fillAttrs[name] ? name : "";
attrs.push({
name: name,
value: value,
escaped: value.replace(/(^|[^\\])"/g, '$1\\\"') //"
});
});
if (handler.start) {
var tagContent = codeContent || ''
handler.start(tagName, attrs, unary, tagContent);
// 重置
codeContent = ''
}
}
}
function parseEndTag(tag, tagName) {
// If no tag name is provided, clean shop
if (!tagName)
var pos = 0;
// Find the closest opened tag of the same type
else {
tagName = tagName.toLowerCase();
for (var pos = stack.length - 1; pos >= 0; pos--)
if (stack[pos] == tagName)
break;
}
if (pos >= 0) {
// Close all the open elements, up the stack
for (var i = stack.length - 1; i >= pos; i--)
if (handler.end)
handler.end(stack[i]);
// Remove the open elements from the stack
stack.length = pos;
}
}
};
function makeMap(str) {
var obj = {}, items = str.split(",");
for (var i = 0; i < items.length; i++)
obj[items[i]] = true;
return obj;
}
export default HTMLParser;
@@ -0,0 +1,53 @@
// 本文件由FirstUI授权予佛山市航电梦联网络科技有限公司(会员ID:11 27,营业执照号: 91 4 40 60 5M A 55 6H1KXH)专用,请尊重知识产权,勿私下传播,违者追究法律责任。
/**
* 获取屏幕的宽高
*/
let windowWidth = 0
let windowHeight = 0
uni.getSystemInfo({
success(res) {
windowWidth = res.windowWidth
windowHeight = res.windowHeight
}
})
const getSystemInfo = () => {
return [ windowWidth, windowHeight ]
}
const bindInstance = () => {
let instance = {}
return {
/**
* 提供键名,绑定对象值
*/
set: (bindName, data = null) => {
if (!instance[bindName]) {
instance[bindName] = data
}
return instance[bindName] || {}
},
get: (bindName) => {
return instance[bindName] || {}
},
/**
* 清除实例对象的所有缓存值
*/
clear: () => {
instance = {}
},
/**
* 清楚实例对象特定的键
*/
remove: (bindName) => {
instance[bindName] && delete instance[bindName]
}
}
};
export default {
getSystemInfo,
cacheInstance: bindInstance(),
}
@@ -0,0 +1,209 @@
// 本文件由FirstUI授权予佛山市航电梦联网络科技有限公司(会员ID:1 1 2 7,营业执照号: 9144 0605 M A 55 6H 1K XH)专用,请尊重知识产权,勿私下传播,违者追究法律责任。
// HTML 支持的数学符号
function strNumDiscode(str) {
str = str.replace(/&forall;/g, '∀');
str = str.replace(/&part;/g, '∂');
str = str.replace(/&exists;/g, '∃');
str = str.replace(/&empty;/g, '∅');
str = str.replace(/&nabla;/g, '∇');
str = str.replace(/&isin;/g, '∈');
str = str.replace(/&notin;/g, '∉');
str = str.replace(/&ni;/g, '∋');
str = str.replace(/&prod;/g, '∏');
str = str.replace(/&sum;/g, '∑');
str = str.replace(/&minus;/g, '');
str = str.replace(/&lowast;/g, '');
str = str.replace(/&radic;/g, '√');
str = str.replace(/&prop;/g, '∝');
str = str.replace(/&infin;/g, '∞');
str = str.replace(/&ang;/g, '∠');
str = str.replace(/&and;/g, '∧');
str = str.replace(/&or;/g, '');
str = str.replace(/&cap;/g, '∩');
str = str.replace(/&cap;/g, '');
str = str.replace(/&int;/g, '∫');
str = str.replace(/&there4;/g, '∴');
str = str.replace(/&sim;/g, '');
str = str.replace(/&cong;/g, '≅');
str = str.replace(/&asymp;/g, '≈');
str = str.replace(/&ne;/g, '≠');
str = str.replace(/&le;/g, '≤');
str = str.replace(/&ge;/g, '≥');
str = str.replace(/&sub;/g, '⊂');
str = str.replace(/&sup;/g, '⊃');
str = str.replace(/&nsub;/g, '⊄');
str = str.replace(/&sube;/g, '⊆');
str = str.replace(/&supe;/g, '⊇');
str = str.replace(/&oplus;/g, '⊕');
str = str.replace(/&otimes;/g, '⊗');
str = str.replace(/&perp;/g, '⊥');
str = str.replace(/&sdot;/g, '⋅');
return str;
}
//HTML 支持的希腊字母
function strGreeceDiscode(str) {
str = str.replace(/&Alpha;/g, 'Α');
str = str.replace(/&Beta;/g, 'Β');
str = str.replace(/&Gamma;/g, 'Γ');
str = str.replace(/&Delta;/g, 'Δ');
str = str.replace(/&Epsilon;/g, 'Ε');
str = str.replace(/&Zeta;/g, 'Ζ');
str = str.replace(/&Eta;/g, 'Η');
str = str.replace(/&Theta;/g, 'Θ');
str = str.replace(/&Iota;/g, 'Ι');
str = str.replace(/&Kappa;/g, 'Κ');
str = str.replace(/&Lambda;/g, 'Λ');
str = str.replace(/&Mu;/g, 'Μ');
str = str.replace(/&Nu;/g, 'Ν');
str = str.replace(/&Xi;/g, 'Ν');
str = str.replace(/&Omicron;/g, 'Ο');
str = str.replace(/&Pi;/g, 'Π');
str = str.replace(/&Rho;/g, 'Ρ');
str = str.replace(/&Sigma;/g, 'Σ');
str = str.replace(/&Tau;/g, 'Τ');
str = str.replace(/&Upsilon;/g, 'Υ');
str = str.replace(/&Phi;/g, 'Φ');
str = str.replace(/&Chi;/g, 'Χ');
str = str.replace(/&Psi;/g, 'Ψ');
str = str.replace(/&Omega;/g, 'Ω');
str = str.replace(/&alpha;/g, 'α');
str = str.replace(/&beta;/g, 'β');
str = str.replace(/&gamma;/g, 'γ');
str = str.replace(/&delta;/g, 'δ');
str = str.replace(/&epsilon;/g, 'ε');
str = str.replace(/&zeta;/g, 'ζ');
str = str.replace(/&eta;/g, 'η');
str = str.replace(/&theta;/g, 'θ');
str = str.replace(/&iota;/g, 'ι');
str = str.replace(/&kappa;/g, 'κ');
str = str.replace(/&lambda;/g, 'λ');
str = str.replace(/&mu;/g, 'μ');
str = str.replace(/&nu;/g, 'ν');
str = str.replace(/&xi;/g, 'ξ');
str = str.replace(/&omicron;/g, 'ο');
str = str.replace(/&pi;/g, 'π');
str = str.replace(/&rho;/g, 'ρ');
str = str.replace(/&sigmaf;/g, 'ς');
str = str.replace(/&sigma;/g, 'σ');
str = str.replace(/&tau;/g, 'τ');
str = str.replace(/&upsilon;/g, 'υ');
str = str.replace(/&phi;/g, 'φ');
str = str.replace(/&chi;/g, 'χ');
str = str.replace(/&psi;/g, 'ψ');
str = str.replace(/&omega;/g, 'ω');
str = str.replace(/&thetasym;/g, 'ϑ');
str = str.replace(/&upsih;/g, 'ϒ');
str = str.replace(/&piv;/g, 'ϖ');
str = str.replace(/&middot;/g, '·');
return str;
}
//
function strcharacterDiscode(str) {
// 加入常用解析
str = str.replace(/&nbsp;/g, '\xa0');
str = str.replace(/&quot;/g, "'");
str = str.replace(/&amp;/g, '&');
// str = str.replace(/&lt;/g, '');
// str = str.replace(/&gt;/g, '');
str = str.replace(/&lt;/g, '<');
str = str.replace(/&gt;/g, '>');
str = str.replace(/&#8226;/g, '•');
return str;
}
// HTML 支持的其他实体
function strOtherDiscode(str) {
str = str.replace(/&OElig;/g, 'Œ');
str = str.replace(/&oelig;/g, 'œ');
str = str.replace(/&Scaron;/g, 'Š');
str = str.replace(/&scaron;/g, 'š');
str = str.replace(/&Yuml;/g, 'Ÿ');
str = str.replace(/&fnof;/g, 'ƒ');
str = str.replace(/&circ;/g, 'ˆ');
str = str.replace(/&tilde;/g, '˜');
str = str.replace(/&ensp;/g, '');
str = str.replace(/&emsp;/g, '');
str = str.replace(/&thinsp;/g, '');
str = str.replace(/&zwnj;/g, '');
str = str.replace(/&zwj;/g, '');
str = str.replace(/&lrm;/g, '');
str = str.replace(/&rlm;/g, '');
str = str.replace(/&ndash;/g, '');
str = str.replace(/&mdash;/g, '—');
str = str.replace(/&lsquo;/g, '');
str = str.replace(/&rsquo;/g, '');
str = str.replace(/&sbquo;/g, '');
str = str.replace(/&ldquo;/g, '“');
str = str.replace(/&rdquo;/g, '”');
str = str.replace(/&bdquo;/g, '„');
str = str.replace(/&dagger;/g, '†');
str = str.replace(/&Dagger;/g, '‡');
str = str.replace(/&bull;/g, '•');
str = str.replace(/&hellip;/g, '…');
str = str.replace(/&permil;/g, '‰');
str = str.replace(/&prime;/g, '');
str = str.replace(/&Prime;/g, '″');
str = str.replace(/&lsaquo;/g, '');
str = str.replace(/&rsaquo;/g, '');
str = str.replace(/&oline;/g, '‾');
str = str.replace(/&euro;/g, '€');
str = str.replace(/&trade;/g, '™');
str = str.replace(/&larr;/g, '←');
str = str.replace(/&uarr;/g, '↑');
str = str.replace(/&rarr;/g, '→');
str = str.replace(/&darr;/g, '↓');
str = str.replace(/&harr;/g, '↔');
str = str.replace(/&crarr;/g, '↵');
str = str.replace(/&lceil;/g, '⌈');
str = str.replace(/&rceil;/g, '⌉');
str = str.replace(/&lfloor;/g, '⌊');
str = str.replace(/&rfloor;/g, '⌋');
str = str.replace(/&loz;/g, '◊');
str = str.replace(/&spades;/g, '♠');
str = str.replace(/&clubs;/g, '♣');
str = str.replace(/&hearts;/g, '♥');
str = str.replace(/&diams;/g, '♦');
str = str.replace(/&#39;/g, '\'');
return str;
}
function strMoreDiscode(str) {
// str = str.replace(/\r\n/g,"");
// str = str.replace(/\n/g,"");
// str = str.replace(/code/g,"wxxxcode-style");
return str;
}
function strDiscode(str) {
str = strNumDiscode(str);
str = strGreeceDiscode(str);
str = strcharacterDiscode(str);
str = strOtherDiscode(str);
str = strMoreDiscode(str);
return str;
}
function urlToHttpUrl(url, rep) {
var patt1 = new RegExp("^//");
var result = patt1.test(url);
if (result) {
url = rep + ":" + url;
}
return url;
}
export default {
strDiscode: strDiscode,
urlToHttpUrl: urlToHttpUrl
}
@@ -0,0 +1,218 @@
<template>
<!--本文件由FirstUI授权予佛山市航电梦联网络科技有限公司会员ID 1 127营业执照号9 14 40 6 0 5 MA 5 56 H 1 K X H专用请尊重知识产权勿私下传播违者追究法律责任-->
<view class="fui-progress__wrap">
<view class="fui-progress__bar-bg"
:style="{ height: height + 'rpx', borderRadius: radius+'rpx', background: background }">
<!-- #ifndef APP-NVUE -->
<view class="fui-progress__bar" :class="{'fui-progress__active-color':!activeColor}"
:style="{background: getActiveColor ,transform:`translate3d(-${translateX},0,0)`,transitionDuration:`${time}s`}">
</view>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<view ref="fui_pg_ani" class="fui-progress__bar" :style="{background: getActiveColor}"></view>
<!-- #endif -->
</view>
<text class="fui-progress__percent"
:style="{ width: percentWidth + 'rpx', fontSize: size + 'rpx', color: color }" v-if="showInfo">
{{ percentage }}%
</text>
</view>
</template>
<script>
// #ifdef APP-NVUE
const animation = uni.requireNativePlugin('animation');
// #endif
export default {
name: 'fui-progress',
emits: ['activeend'],
props: {
percent: {
type: [Number, String],
default: 0
},
height: {
type: [Number, String],
default: 8
},
// #ifndef APP-NVUE
radius: {
type: [Number, String],
default: 8
},
// #endif
// #ifdef APP-NVUE
radius: {
type: [Number, String],
default: 0
},
// #endif
showInfo: {
type: Boolean,
default: false
},
//右侧百分比字体大小 rpx
size: {
type: [Number, String],
default: 28
},
//右侧百分比颜色
color: {
type: String,
default: '#333'
},
//右侧百分比宽度
percentWidth: {
type: [Number, String],
default: 96
},
//未选择的进度条的颜色
background: {
type: String,
default: '#CCCCCC'
},
//已选进度条颜色,可渐变
activeColor: {
type: String,
default: ''
},
//进度增加1%所需毫秒数
duration: {
type: [Number, String],
default: 15
}
},
watch: {
percent(val) {
this.darwProgress();
}
},
mounted() {
this.$nextTick(() => {
this.darwProgress();
})
},
computed: {
getActiveColor() {
let color = this.activeColor;
// #ifdef APP-NVUE
if (!color || color === true) {
const app = uni && uni.$fui && uni.$fui.color;
color = (app && app.primary) || '#465CFF';
}
// #endif
return color;
}
},
data() {
return {
percentage: 0,
translateX: '-100%',
time: 0
};
},
methods: {
// #ifdef APP-NVUE
_animation(translateX, duration) {
if (!this.$refs['fui_pg_ani']) return;
animation.transition(
this.$refs['fui_pg_ani'].ref, {
styles: {
transform: `translateX(-${translateX})`
},
duration: duration * 1000,
timingFunction: 'linear',
needLayout: false,
delay: 0 //ms
},
() => {
this.$emit('activeend', {});
}
);
},
// #endif
darwProgress() {
let percent = Number(this.percent);
percent = percent > 100 ? 100 : percent;
this.time = Number(this.duration) * Math.abs(percent - this.percentage) / 1000
if (percent < this.percentage && (this.percentage - percent) > 30) {
//后百分比数大于30时 时间缩短
this.time = this.time / 2
}
this.percentage = percent;
this.translateX = (100 - percent) + '%';
// #ifndef APP-NVUE
setTimeout(() => {
this.$emit('activeend', {});
}, this.time)
// #endif
// #ifdef APP-NVUE
this._animation(this.translateX, this.time)
// #endif
}
}
};
</script>
<style scoped>
.fui-progress__wrap {
/* #ifndef APP-NVUE */
width: 100%;
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
}
/* nvue android 端 overflow: hidden 无效,目前uni-app官方尚未修复此问题*/
.fui-progress__bar-bg {
/* #ifndef APP-NVUE */
width: 100%;
transform: translateZ(0);
/* #endif */
flex: 1;
position: relative;
overflow: hidden;
}
.fui-progress__bar {
/* #ifndef APP-NVUE */
width: 100%;
z-index: 2;
/* #endif */
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
/* #ifndef APP-NVUE */
transform: translate3d(-100%, 0, 0);
transition-delay: 0s;
transition-property: transform;
transition-timing-function: linear;
/* #endif */
/* #ifdef APP-NVUE */
transform: translateX(-100%);
/* #endif */
transition-duration: 0s;
}
.fui-progress__percent {
text-align: center;
/* #ifndef APP-NVUE */
display: block;
flex-shrink: 0;
/* #endif */
}
/* #ifndef APP-NVUE */
.fui-progress__active-color {
background: var(--fui-color-primary, #465CFF) !important;
}
/* #endif */
</style>
+166
View File
@@ -0,0 +1,166 @@
<template>
<!--本文件由FirstUI授权予佛山市航电梦联网络科技有限公司会员ID 112 7营业执照号 9 1 4406 0 5MA55 6 H 1KX H专用请尊重知识产权勿私下传播违者追究法律责任-->
<view class="fui-row__box" :class="[flex?'fui-row__flex':'', justifyClass,alignClass]" :style="{marginTop:marginTop,marginBottom:marginBottom,marginLeft:`-${marginValue}rpx`,
marginRight:`-${marginValue}rpx`}">
<slot></slot>
</view>
</template>
<script>
export default {
name: "fui-row",
componentName: 'fuiRow',
props: {
//是否为flex布局
isFlex: {
type: Boolean,
default: false
},
//flex 布局下的水平排列方式 start/end/center/space-around/space-between
justify: {
type: String,
default: 'start'
},
//flex 布局下的垂直排列方式 top/middle/bottom
align: {
type: String,
default: 'top'
},
marginTop: {
type: String,
default: '0'
},
marginBottom: {
type: String,
default: '0'
},
//栅格间隔
gutter: {
type: Number,
default: 0
},
// nvue如果使用span等属性,需要配置宽度
width: {
type: [String, Number],
default: 750
}
},
data() {
return {
flex: false
}
},
watch: {
isFlex(val) {
// #ifndef APP-NVUE
this.flex = val;
// #endif
}
},
created() {
// #ifndef APP-NVUE
this.flex = this.isFlex;
// #endif
// #ifdef APP-NVUE
this.flex = true;
// #endif
},
computed: {
marginValue() {
// #ifndef APP-NVUE
if (this.gutter) {
return Number(this.gutter) / 2;
}
// #endif
return 0;
},
justifyClass() {
return this.justify !== 'start' ? `fui-row__${this.justify}` : ''
},
alignClass() {
return this.align !== 'top' ? `fui-row__${this.align}` : ''
}
}
}
</script>
<style scoped>
/* #ifdef MP-WEIXIN || MP-TOUTIAO || MP-QQ */
:host {
position: relative;
box-sizing: border-box;
display: block;
}
/* #endif */
.fui-row__box {
/* #ifdef APP-NVUE */
flex: 1;
/* #endif */
/* #ifndef APP-NVUE */
box-sizing: border-box;
-webkit-box-orient: horizontal;
-webkit-box-direction: normal;
/* #endif */
position: relative;
/* #ifdef MP-TOUTIAO || MP-QQ || MP-BAIDU */
display: block;
/* #endif */
flex-direction: row;
}
/* #ifndef APP-NVUE */
.fui-row__box::before {
display: table;
content: " ";
}
.fui-row__box::after {
display: table;
content: " ";
}
.fui-row__box::after {
clear: both;
}
/* #endif */
.fui-row__flex {
/* #ifndef APP-NVUE*/
display: flex;
/* #endif */
flex-direction: row;
}
.fui-row__middle {
align-items: center;
}
.fui-row__bottom {
align-items: flex-end;
}
/* #ifndef APP-NVUE */
.fui-row__before {
display: table
}
/* #endif */
.fui-row__end {
justify-content: flex-end;
}
.fui-row__center {
justify-content: center;
}
.fui-row__space-around {
justify-content: space-around;
}
.fui-row__space-between {
justify-content: space-between;
}
</style>
@@ -0,0 +1,205 @@
<template>
<!--本文件由FirstUI授权予佛山市航电梦联网络科技有限公司会员ID1 127营业执照号91 4 40 60 5 M A5 56 H 1KXH专用请尊重知识产权勿私下传播违者追究法律责任-->
<view class="fui-sections__wrap"
:style="{marginTop:marginTop+'rpx',marginBottom:marginBottom+'rpx',background:background,paddingTop:padding[0] || 0,paddingRight:padding[1]||0,paddingBottom:padding[2] || padding[0]||0,paddingLeft:padding[3] || padding[1]||0}">
<view class="fui-sections__title" @tap="handleClick">
<view class="fui-sections__line" :class="{'fui-sections__line-color':!getLineColor}"
:style="{background:getLineColor,width:lineWidth,top:lineGap+'rpx',bottom:lineGap+'rpx',borderRadius:lineCap==='circle'?lineWidth:0,left:getLeft}"
v-if="isLine">
</view>
<slot></slot>
<text class="fui-sections__title-text" :style="getTitleStyle" v-if="title">{{title}}</text>
<slot name="right"></slot>
</view>
<view class="fui-sections__descr" :style="getDescrTop" v-if="descr">
<text class="fui-sections__descr-text" :style="getDescrStyle">{{descr}}</text>
</view>
<slot name="descr"></slot>
</view>
</template>
<script>
export default {
name: "fui-section",
emits: ['click'],
props: {
title: {
type: String,
default: ''
},
//默认使用全局配置值
size: {
type: [Number, String],
default: 0
},
lineHeight: {
type: [Number, String],
default: 0
},
color: {
type: String,
default: ''
},
fontWeight: {
type: [Number, String],
default: 0
},
descr: {
type: String,
default: ''
},
descrSize: {
type: [Number, String],
default: 0
},
descrColor: {
type: String,
default: ''
},
descrTop: {
type: [Number, String],
default: 0
},
isLine: {
type: Boolean,
default: false
},
lineWidth: {
type: String,
default: '2px'
},
lineColor: {
type: String,
default: ''
},
//square、circle
lineCap: {
type: String,
default: 'circle'
},
//nvue android端不支持负数
lineRight: {
type: [Number, String],
default: 16
},
lineGap: {
type: [Number, String],
default: 0
},
background: {
type: String,
default: 'transparent'
},
padding: {
type: Array,
default () {
return ['0', '32rpx']
}
},
marginTop: {
type: [Number, String],
default: 0
},
marginBottom: {
type: [Number, String],
default: 0
}
},
computed: {
getLineColor() {
let color = this.lineColor;
// #ifdef APP-NVUE
if (!color || color === true) {
color = (uni && uni.$fui && uni.$fui.color.primary) || '#465CFF';
}
// #endif
return color;
},
getLeft() {
const left = Number(this.lineRight || 0)
return `${left >0 ? 0 :left}rpx`
},
getTitleStyle() {
const app = uni && uni.$fui && uni.$fui.fuiSection;
const size = this.size || (app && app.size) || 32;
const color = this.color || (app && app.color) || '#181818';
const weight = this.fontWeight || (app && app.fontWeight) || 600;
const left = Number(this.lineRight || 0)
let style =
`font-size:${size}rpx;color:${color};font-weight:${weight};padding-left:${left<=0 || !this.isLine ? 0 :left}rpx;`
style += `line-height:${this.lineHeight == 0?size:this.lineHeight}rpx`
return style;
},
getDescrStyle() {
const app = uni && uni.$fui && uni.$fui.fuiSection;
const size = this.descrSize || (app && app.descrSize) || 28;
const color = this.descrColor || (app && app.descrColor) || '#B2B2B2';
return `font-size:${size}rpx;color:${color};`
},
getDescrTop() {
const app = uni && uni.$fui && uni.$fui.fuiSection;
return 'padding-top:' + (this.descrTop || (app && app.descrTop) || 8) + 'rpx;'
}
},
methods: {
handleClick() {
this.$emit('click', {
title: this.title
})
}
}
}
</script>
<style scoped>
/* 全局样式中包含 fui-section 避免影响*/
.fui-sections__wrap {
/* #ifndef APP-NVUE */
width: 100%;
box-sizing: border-box;
/* #endif */
}
.fui-sections__title {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
word-break: break-all;
flex-shrink: 0;
/* #endif */
flex-direction: row;
align-items: center;
}
.fui-sections__title-text {
/* #ifndef APP-NVUE */
word-break: break-all;
/* #endif */
}
.fui-sections__line {
position: absolute;
left: 0;
}
.fui-sections__descr {
/* #ifndef APP-NVUE */
word-break: break-all;
/* #endif */
}
.fui-sections__descr-text {
/* #ifndef APP-NVUE */
word-break: break-all;
/* #endif */
font-weight: 400;
}
/* #ifndef APP-NVUE */
.fui-sections__line-color {
background: var(--fui-color-primary, #465CFF) !important;
}
/* #endif */
</style>
@@ -0,0 +1,190 @@
<template>
<!--本文件由FirstUI授权予佛山市航电梦联网络科技有限公司会员ID 1 127营业执照号91 4 4 0 6 05 M A5 56H 1 KX H专用请尊重知识产权勿私下传播违者追究法律责任-->
<view class="fui-sticky__wrap" :class="{'fui-sticky__fixed':!range}" :style="getStyles" :id="elId"
ref="fui_sticky__el">
<view :class="{'fui-sticky__fixed':range,'fui-sticky__fixed-mp':!range && isFixed}" :style="getStyle">
<slot></slot>
</view>
<!-- #ifdef MP-BAIDU || MP-QQ || MP-TOUTIAO -->
<view class="fui-sticky__seat" :class="{'fui-sticky__seat-hidden':!isFixed}" v-if="!range"
:style="{height:height+'px'}"></view>
<!-- #endif -->
<slot name="content"></slot>
</view>
</template>
<script>
// #ifdef APP-NVUE
const dom = weex.requireModule('dom')
// #endif
export default {
name: "fui-sticky",
emits: ['sticky'],
// #ifdef MP-WEIXIN
options: {
virtualHost: true
},
// #endif
props: {
top: {
type: [Number, String],
default: 0
},
range: {
type: Boolean,
default: false
},
scrollTop: {
type: Number,
default: 0
},
zIndex: {
type: [Number, String],
default: 999
},
width: {
type: [Number, String],
default: 750
},
param: {
type: [Number, String],
default: 0
}
},
computed: {
getStyles() {
let styles = ''
// #ifdef APP-NVUE
styles = `width:${this.width}rpx;`
// #endif
if (!this.range) {
styles += `top:${this.top}px;z-index:${this.zIndex};`
}
return styles
},
getStyle() {
let styles = ''
if (this.range) {
styles = `top:${this.top}px;z-index:${this.zIndex};`
}
return styles
}
},
watch: {
scrollTop(val) {
this.updateStickyChange();
}
},
mounted() {
this.$nextTick(()=>{
setTimeout(() => {
this.init(() => {
this.updateStickyChange();
});
}, 50)
})
},
updated(e) {
this.$nextTick(() => {
this.init(() => {
this.updateStickyChange();
});
})
},
data() {
const elId = `fui_${Math.ceil(Math.random() * 10e5).toString(36)}`
return {
elId: elId,
timer: null,
elTop: 0,
height: 0,
isFixed: false
};
},
methods: {
updateStickyChange() {
const et = this.elTop;
const h = this.height;
const st = this.scrollTop
const t = this.top
if (this.range) {
this.isFixed = (st + t >= et && st + t < et + h) ? true : false
} else {
this.isFixed = st + t >= et ? true : false
}
//是否吸顶
this.$emit("sticky", {
isFixed: this.isFixed,
param: this.param
})
},
init(callback) {
// #ifndef APP-NVUE
const elId = `#${this.elId}`;
uni.createSelectorQuery()
// #ifndef MP-ALIPAY
.in(this)
// #endif
.select(elId)
.fields({
size: true,
rect: true
}, res => {
if (res) {
this.elTop = res.top + (this.scrollTop || 0);
this.height = res.height;
callback && callback()
}
}).exec()
// #endif
// #ifdef APP-NVUE
dom.getComponentRect(this.$refs['fui_sticky__el'], option => {
if (option && option.result && option.size) {
this.height = option.size.height + 1
this.elTop = option.size.top + (this.scrollTop || 0)
callback && callback()
}
})
// #endif
}
}
}
</script>
<style scoped>
.fui-sticky__wrap {
/* #ifndef APP-VUE */
width: 100%;
/* #endif */
}
.fui-sticky__fixed {
/* #ifndef APP-NVUE */
width: 100%;
/* #endif */
position: sticky;
top: 0;
}
/* #ifdef MP-QQ || MP-BAIDU || MP-TOUTIAO */
.fui-sticky__fixed-mp {
width: 100%;
position: fixed;
top: 0;
left: 0;
}
.fui-sticky__seat {
width: 100%;
}
.fui-sticky__seat-hidden {
position: fixed;
left: -9999px;
z-index: -10;
visibility: hidden;
}
/* #endif */
</style>
@@ -0,0 +1,558 @@
<template>
<!--本文件由FirstUI授权予佛山市航电梦联网络科技有限公司会员ID11 2 7营业执照号 9 1 4 406 0 5MA 55 6H1 KXH专用请尊重知识产权勿私下传播违者追究法律责任-->
<scroll-view class="fui-tabs__scrollbox"
:class="{'fui-tabs__fixed':isFixed && !isSticky,'fui-tabs__sticky':isSticky}" :scroll-with-animation="true"
:scroll-x="scroll" :show-scrollbar="false" :scroll-into-view="scrollInto"
:style="{background:background,zIndex:(isFixed || isSticky)?zIndex:1,top: isFixed || isSticky ? top + 'px' : 'auto'}">
<view class="fui-scroll__view" :class="{'fui-tabs__full':!alignLeft}">
<view v-for="(tab, index) in vals" :key="index" class="fui-tabs__item"
:class="{'fui-tabs__full':!alignLeft}"
:style="{paddingLeft:itemPadding+'rpx',paddingRight:itemPadding+'rpx'}" :id="tab.fui_s_id"
@tap="switchTab(index)">
<view class="fui-tabs__text-wrap"
:class="{'fui-tabs__wrap-disabled':tab[disabledKey],'fui-tabs__item-column':direction==='column' && tab.icon}"
:style="{height:height+'rpx'}">
<view class="fui-tabs__line-wrap" :class="{'fui-tabs__line-center':center}"
:style="{bottom:bottom +'rpx',left:`-${padding}rpx`,right:`-${padding}rpx`}" v-if="isSlider">
<view class="fui-tabs__ac-line"
:class="{'fui-tabs__line-short':short,'fui-tabs__full':!short,'fui-tabs__slider-color':!getSliderBgColor}"
:style="{height:sliderHeight+'rpx',background:getSliderBgColor,borderRadius:sliderRadius==-1?sliderHeight+'rpx':sliderRadius+'rpx',transform: `scale(${tabIndex===index?(isNvue?1:scale):(isNvue?0.00001:0)})`}">
</view>
</view>
<image class="fui-tabs__icon" :class="{'fui-tabs__icon-column':direction==='column'}"
:src="tabIndex===index && tab.selectedIcon?tab.selectedIcon:tab.icon" v-if="tab.icon">
</image>
<!-- #ifdef APP-NVUE -->
<view class="fui-tabs__text">
<text
:style="{fontSize:selectedSize+'rpx',fontWeight:tabIndex===index?selectedFontWeight:fontWeight,height:height+'rpx',lineHeight:height+'rpx'}"
style="opacity: 0;">{{tab[nameKey]}}</text>
<text class="fui-tabs__text-nvue"
:class="{'fui-tabs__selected-color':!getSelectedColor && tabIndex===index,'fui-tabs__text-color':!color && tabIndex!==index}"
:style="{fontSize:(tabIndex===index && isNvue? selectedSize:size)+'rpx',color:tabIndex===index?getSelectedColor:color,fontWeight:tabIndex===index?selectedFontWeight:fontWeight,height:height+'rpx',lineHeight:height+'rpx'}">{{tab[nameKey]}}</text>
<text
:class="{'fui-tabs__badge-color':!getBadgeBgColor,'fui-tabs__badge-dot':isDot,'fui-tabs__badge':!isDot}"
:style="{color:badgeColor,background:getBadgeBgColor,top:getTop+'rpx'}"
v-if="tab[badgeKey]">{{isDot?'':tab[badgeKey]}}</text>
</view>
<!-- #endif -->
<!-- #ifndef APP-NVUE -->
<!--vue3中text嵌套text使用v-if会显示v-if文本-->
<view class="fui-tabs__text"
:class="{'fui-tabs__selected-color':!getSelectedColor && tabIndex===index,'fui-tabs__text-color':!color && tabIndex!==index}"
:style="{fontSize:(tabIndex===index && isNvue? selectedSize:size)+'rpx',color:tabIndex===index?getSelectedColor:color,fontWeight:tabIndex===index?selectedFontWeight:fontWeight,transform:`scale(${tabIndex===index && !isNvue?scale:1})`}">
{{tab[nameKey]}}<text
:class="{'fui-tabs__badge-color':!getBadgeBgColor,'fui-tabs__badge-dot':isDot,'fui-tabs__badge':!isDot}"
:style="{color:badgeColor,background:getBadgeBgColor}"
v-if="tab[badgeKey]">{{isDot?'':tab[badgeKey]}}</text>
</view>
<!-- #endif -->
</view>
</view>
</view>
</scroll-view>
</template>
<script>
export default {
name: 'fui-tabs',
emits: ['change'],
// #ifdef MP-WEIXIN
options: {
virtualHost: true
},
// #endif
props: {
// 标签页数据源
tabs: {
type: Array,
default () {
return []
}
},
nameKey: {
type: String,
default: 'name'
},
badgeKey: {
type: String,
default: 'badge'
},
disabledKey: {
type: String,
default: 'disabled'
},
// 当前选项卡
current: {
type: Number,
default: 0
},
// 是否可以滚动
scroll: {
type: Boolean,
default: false
},
// tab高度 rpx
height: {
type: [Number, String],
default: 96
},
background: {
type: String,
default: '#fff'
},
//字体大小
size: {
type: [Number, String],
default: 28
},
//字体颜色
// #ifdef APP-NVUE
color: {
type: String,
default: '#7F7F7F'
},
// #endif
// #ifndef APP-NVUE
color: {
type: String,
default: ''
},
// #endif
//选中前字重
fontWeight: {
type: [Number, String],
default: 'normal'
},
//仅Nvue端生效,代替scale属性
selectedSize: {
type: [Number, String],
default: 32
},
//选中后字体颜色
selectedColor: {
type: String,
default: ''
},
//选中后字重
selectedFontWeight: {
type: [Number, String],
default: 500
},
//选中后字体缩放倍数
//Nvue端 Android 暂不支持设置overflow:visible,放大后文字超出部分被隐藏
scale: {
type: [Number, String],
default: 1.2
},
badgeColor: {
type: String,
default: '#fff'
},
badgeBackground: {
type: String,
default: ''
},
isDot: {
type: Boolean,
default: false
},
isSlider: {
type: Boolean,
default: true
},
//滑块高度
sliderHeight: {
type: [Number, String],
default: 5
},
//滑块背景颜
sliderBackground: {
type: String,
default: ''
},
//滑块 radius
sliderRadius: {
type: [Number, String],
default: -1
},
//滑块左右padding值
padding: {
type: [Number, String],
default: 0
},
//滑块bottom
bottom: {
type: [Number, String],
default: 0
},
//滑块是否固定为较短的长度45rpx
short: {
type: Boolean,
default: true
},
//滑块是否居中显示
center: {
type: Boolean,
default: false
},
//是否固定
isFixed: {
type: Boolean,
default: false
},
//吸顶效果,为true时isFixed失效
isSticky: {
type: Boolean,
default: false
},
//isFixed或isSticky为true时,tabs top值 px
// #ifndef H5
top: {
type: [Number, String],
default: 0
},
// #endif
// #ifdef H5
top: {
type: [Number, String],
default: 44
},
// #endif
//当数据不满一屏时,item项是否靠左对齐,默认均分铺满
alignLeft: {
type: Boolean,
default: false
},
//tabs item项排列方式:row、column
direction: {
type: String,
default: 'row'
},
itemPadding: {
type: [Number, String],
default: 32
},
zIndex: {
type: [Number, String],
default: 996
}
},
watch: {
tabs(vals) {
this.initData(vals)
},
current(newVal, oldVal) {
this.switchTab(newVal);
}
},
created() {
this.initData(this.tabs)
},
computed: {
// #ifdef APP-NVUE
getTop() {
const height = Number(this.height) - Number(this.selectedSize)
return height / 2
},
// #endif
getSelectedColor() {
let color = this.selectedColor
// #ifdef APP-NVUE
if (!color || color === true) {
const app = uni && uni.$fui && uni.$fui.color;
color = (app && app.primary) || '#465CFF';
}
// #endif
return color
},
getSliderBgColor() {
let color = this.sliderBackground
// #ifdef APP-NVUE
if (!color || color === true) {
const app = uni && uni.$fui && uni.$fui.color;
color = (app && app.primary) || '#465CFF';
}
// #endif
return color
},
getBadgeBgColor() {
let color = this.badgeBackground
// #ifdef APP-NVUE
if (!color || color === true) {
const app = uni && uni.$fui && uni.$fui.color;
color = (app && app.danger) || '#FF2B2B';
}
// #endif
return color
}
},
data() {
let isNvue = false;
// #ifdef APP-NVUE
isNvue = true;
// #endif
return {
vals: [],
scrollInto: '',
tabIndex: 0,
isNvue: isNvue
};
},
methods: {
getId() {
return `fui_${Math.ceil(Math.random() * 10e5).toString(36)}`
},
initData(vals) {
if (vals && vals.length > 0) {
if (typeof vals[0] === 'object') {
vals.map(item => {
const scrollId = this.getId()
item.fui_s_id = scrollId;
});
} else {
//字符串
vals = vals.map(item => {
const scrollId = this.getId()
return {
[this.nameKey]: item,
fui_s_id: scrollId
}
})
}
this.vals = vals;
this.$nextTick(() => {
setTimeout(() => {
this.switchTab(this.current)
}, 50)
})
}
},
switchTab(index) {
const item = {
...this.vals[index]
}
if (this.tabIndex === index || item[this.disabledKey]) return;
this.tabIndex = index;
let scrollIndex = index - 1 < 0 ? 0 : index - 1;
this.scrollInto = this.vals[scrollIndex].fui_s_id;
delete item.fui_s_id;
this.$emit('change', {
index: index,
...item
})
}
}
};
</script>
<style scoped>
/* #ifndef APP-NVUE */
::-webkit-scrollbar {
width: 0 !important;
height: 0 !important;
color: transparent !important;
display: none;
}
/* #endif */
.fui-tabs__scrollbox {
/* #ifndef APP-NVUE */
width: 100%;
/* #endif */
flex: 1;
flex-direction: row;
overflow: hidden;
}
.fui-tabs__fixed {
position: fixed;
left: 0;
right: 0;
}
.fui-tabs__sticky {
position: sticky;
left: 0;
right: 0;
}
.fui-scroll__view {
/* #ifndef APP-NVUE */
min-width: 100%;
white-space: nowrap;
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
}
.fui-tabs__item {
/* #ifndef APP-NVUE */
display: flex;
flex-shrink: 0;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
position: relative;
}
.fui-tabs__full {
flex: 1;
}
.fui-tabs__text-wrap {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
z-index: 3;
}
.fui-tabs__wrap-disabled {
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
opacity: 0.5;
}
.fui-tabs__icon {
width: 40rpx;
height: 40rpx;
margin-right: 12rpx;
}
.fui-tabs__item-column {
flex-direction: column !important;
}
.fui-tabs__icon-column {
margin-right: 0 !important;
margin-bottom: 8rpx;
}
.fui-tabs__text {
/* #ifndef APP-NVUE */
white-space: nowrap;
display: block;
transition: transform 0.2s linear;
z-index: 3;
/* #endif */
/* #ifdef APP-NVUE */
flex-wrap: nowrap;
flex-direction: row;
align-items: center;
justify-content: center;
/* #endif */
position: relative;
}
/* #ifdef APP-NVUE */
.fui-tabs__text-nvue {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
text-align: center;
}
/* #endif */
.fui-tabs__badge {
height: 36rpx;
padding: 0 12rpx;
color: #FFFFFF;
font-size: 24rpx;
line-height: 36rpx;
border-radius: 100px;
position: absolute;
/* #ifndef APP-NVUE */
min-width: 36rpx !important;
display: flex;
box-sizing: border-box;
right: -32rpx;
top: -18rpx;
z-index: 10;
/* #endif */
/* #ifdef APP-NVUE */
right: 0;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
transform: scale(0.9);
}
.fui-tabs__badge-dot {
height: 8px !important;
width: 8px !important;
/* #ifdef APP-NVUE */
border-radius: 100px;
/* #endif */
position: absolute;
/* #ifndef APP-NVUE */
display: inline-block;
right: -6px;
top: -3px;
border-radius: 50%;
z-index: 10;
/* #endif */
/* #ifdef APP-NVUE */
right: 0;
/* #endif */
}
.fui-tabs__line-wrap {
position: absolute;
border-radius: 2px;
z-index: 2;
flex: 1;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.fui-tabs__line-center {
justify-content: center;
left: 0;
}
.fui-tabs__ac-line {
transition: transform 0.2s linear;
}
.fui-tabs__line-short {
width: 45rpx !important;
}
/* #ifndef APP-NVUE */
.fui-tabs__selected-color {
color: var(--fui-color-primary, #465CFF) !important;
}
.fui-tabs__text-color {
color: var(--fui-color-subtitle, #7F7F7F) !important;
}
.fui-tabs__slider-color {
background: var(--fui-color-primary, #465CFF) !important;
}
.fui-tabs__badge-color {
background: var(--fui-color-danger, #FF2B2B) !important;
}
/* #endif */
</style>
@@ -0,0 +1,109 @@
<template>
<!--本文件由FirstUI授权予佛山市航电梦联网络科技有限公司会员ID1 127营业执照号 9 1 4 4 0 60 5M A556H1K X H专用请尊重知识产权勿私下传播违者追究法律责任-->
<view class="fui-timeaxis__node-wrap">
<view class="fui-timeaxis__left" :style="{width:leftWidth+'rpx'}">
<slot name="left"></slot>
</view>
<view class="fui-timeaxis__node-box" :style="{width:width+'rpx'}">
<view class="fui-timeaxis__node">
<slot></slot>
</view>
<view class="fui-timeaxis__line" :style="{background:lineColor,width:lineWidth+'px'}" v-if="lined"></view>
</view>
<view class="fui-timeaxis__content">
<slot name="right"></slot>
</view>
</view>
</template>
<script>
export default {
name: "fui-timeaxis-node",
inject: ['timeaxis'],
props: {
lined: {
type: Boolean,
default: true
},
lineColor: {
type: String,
default: '#ccc'
}
},
data() {
return {
lineWidth: 1,
width: 48,
leftWidth: 0
};
},
created() {
this.init()
},
methods: {
init() {
if (this.timeaxis) {
this.width = this.timeaxis.width
this.lineWidth = this.timeaxis.lineWidth
this.leftWidth = this.timeaxis.leftWidth
this.timeaxis.children.push(this)
}
}
}
}
</script>
<style scoped>
.fui-timeaxis__node-wrap {
position: relative;
/* #ifndef APP-NVUE */
width: 100%;
display: flex;
/* #endif */
flex-direction: row;
}
.fui-timeaxis__line {
/* #ifdef APP-NVUE */
width: 0.5px;
/* #endif */
/* #ifndef APP-NVUE */
width: 1px;
transform: scaleX(.5) translateZ(0);
transform-origin: center center;
/* #endif */
flex: 1;
}
.fui-timeaxis__node-box {
/* #ifndef APP-NVUE */
display: flex;
flex-shrink: 0;
/* #endif */
flex-direction: column;
align-items: center;
overflow: hidden;
}
.fui-timeaxis__node {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
align-items: center;
justify-content: center;
}
.fui-timeaxis__left {
/* #ifndef APP-NVUE */
flex-shrink: 0;
/* #endif */
overflow: hidden;
}
.fui-timeaxis__content {
/* #ifndef APP-NVUE */
width: 100%;
/* #endif */
flex: 1;
}
</style>
@@ -0,0 +1,80 @@
<template>
<!--本文件由FirstUI授权予佛山市航电梦联网络科技有限公司会员ID 1 1 27营业执照号 91 4 4 06 0 5MA 556H1 K X H专用请尊重知识产权勿私下传播违者追究法律责任-->
<view class="fui-timeaxis__wrap"
:style="{paddingTop:padding[0] || 0,paddingRight:padding[1]||0,paddingBottom:padding[2] || padding[0]||0,paddingLeft:padding[3] || padding[1]||0,background:background}">
<slot></slot>
</view>
</template>
<script>
export default {
name: "fui-timeaxis",
props: {
padding: {
type: Array,
default () {
return []
}
},
background: {
type: String,
default: 'transparent'
},
leftWidth: {
type: [Number, String],
default: 0
},
width: {
type: [Number, String],
default: 48
},
// #ifdef APP-NVUE
lineWidth: {
type: [Number, String],
default: 0.5
},
// #endif
// #ifndef APP-NVUE
lineWidth: {
type: [Number, String],
default: 1
}
// #endif
},
provide() {
return {
timeaxis: this
}
},
created() {
this.children = []
},
watch: {
width(val) {
this.children.forEach(item => {
item.width = val
})
},
lineWidth(val) {
this.children.forEach(item => {
item.lineWidth = val
})
},
leftWidth(val) {
this.children.forEach(item => {
item.leftWidth = val
})
}
}
}
</script>
<style scoped>
.fui-timeaxis__wrap {
/* #ifndef APP-NVUE */
width: 100%;
box-sizing: border-box;
/* #endif */
}
</style>
@@ -0,0 +1,236 @@
<template>
<!--本文件由FirstUI授权予佛山市航电梦联网络科技有限公司会员ID 11 27营业执照号 91 44 06 05 M A55 6 H 1K XH专用请尊重知识产权勿私下传播违者追究法律责任-->
<view :id="elId" ref="fui_wi__el" class="fui-waterfall__item-wrap" :class="{'fui-waterfall__item-show':isShow}"
:style="{width:width+'px' ,background:background,borderRadius:radius+'rpx',transform:transform}"
@tap="handleTap">
<slot name="upper"></slot>
<view :class="{'fui-waterfall__img-wrap':!isLoaded,'fui-waterfall__hidden':imgHeight!=0}" :style="getStyl"
v-if="isSrc">
<image class="fui-waterfall__img" :src="src" :mode="imgHeight!=0?'scaleToFill':'widthFix'" :webp="webp"
:draggable="draggable" @load="handleLoad" @error="handleError" :style="getStyles" v-if="isLoaded">
</image>
</view>
<slot></slot>
</view>
</template>
<script>
// #ifdef APP-NVUE
const dom = weex.requireModule('dom')
const animation = uni.requireNativePlugin('animation');
// #endif
export default {
name: "fui-waterfall-item",
emits: ['click'],
inject: ['waterfall'],
// #ifdef MP-WEIXIN
options: {
virtualHost: true
},
// #endif
props: {
background: {
type: String,
default: '#FFFFFF'
},
radius: {
type: [Number, String],
default: 16
},
src: {
type: String,
default: ''
},
imgWidth: {
type: [Number, String],
default: 0
},
//V1.9.8+ 设置图片高度,则不再等图片加载完成后再去渲染
imgHeight: {
type: [Number, String],
default: 0
},
webp: {
type: Boolean,
default: false
},
draggable: {
type: Boolean,
default: true
},
param: {
type: [Number, String],
default: 0
}
},
created() {
this.src && (this.isSrc = true);
if (this.waterfall) {
this.waterfall.childrenArr.push(this)
if (this.waterfall.itemWidth) {
this.width = this.waterfall.itemWidth
} else {
this.waterfall.initParam((width) => {
this.width = width
})
}
}
},
computed: {
getStyles() {
const width = this.imgWidth != 0 ? `${this.imgWidth}rpx` : `${this.width}px`
let style =
`width:${width};border-radius:${this.radius}rpx ${this.radius}rpx 0 0;`
if (this.imgHeight != 0) {
style += `height:${this.imgHeight}rpx;`
}
return style;
},
getStyl() {
let style = this.getStyles;
if (this.imgHeight == 0 && !this.isLoaded) {
style += `height:${this.width}px;`
}
return style;
}
},
mounted() {
if (!this.src || this.imgHeight != 0) {
this.$nextTick(() => {
setTimeout(() => {
this.getWaterfallItemInfo()
}, 50)
})
}
},
data() {
const elId = `fui_${Math.ceil(Math.random() * 10e5).toString(36)}`
return {
elId: elId,
width: 0,
height: 0,
transform: '',
isShow: false,
isLoaded: true,
isSrc: false
};
},
methods: {
// #ifdef APP-NVUE
aniTransForm(x, y) {
animation.transition(
this.$refs['fui_wi__el'].ref, {
styles: {
transform: `translate(${x}px,${y}px)`
},
duration: 0,
timingFunction: 'ease-in-out',
needLayout: false,
delay: 0 //ms
},
() => {}
);
},
// #endif
getWaterfallItemInfo() {
this.getItemHeight((res) => {
if (this.waterfall) {
this.waterfall.loadedArr.push('ok')
if (this.waterfall.childrenArr.length === this.waterfall.loadedArr.length) {
this.waterfall.startSorting()
}
}
})
},
getItemHeight(callback, index = 0) {
// #ifdef APP-NVUE
const result = dom.getComponentRect(this.$refs['fui_wi__el'], option => {
if (option && option.result && option.size) {
this.height = parseInt(option.size.height + 1)
callback && callback(this.height)
}
})
// #endif
// #ifndef APP-NVUE
uni.createSelectorQuery()
// #ifndef MP-ALIPAY
.in(this)
// #endif
.select(`#${this.elId}`)
.fields({
size: true
}, data => {
if (index >= 20) return
if (data && data.height) {
this.height = data.height
callback && callback(data.height)
} else {
index++
setTimeout(() => {
this.getItemHeight(callback, index)
}, 50)
return
}
})
.exec()
// #endif
},
handleLoad(e) {
if (this.imgHeight != 0) return;
setTimeout(() => {
this.getWaterfallItemInfo()
}, 50)
},
handleError(e) {
this.isLoaded = false
if (this.imgHeight != 0) return;
setTimeout(() => {
this.getWaterfallItemInfo()
}, 50)
},
handleTap() {
this.$emit('click', {
param: this.param
})
}
}
}
</script>
<style scoped>
.fui-waterfall__item-wrap {
position: absolute;
left: 0;
top: 0;
/* #ifndef APP-NVUE */
display: flex;
box-sizing: border-box;
/* #endif */
overflow: hidden;
opacity: 0;
transition-property: opacity;
transition-duration: .5s;
flex-direction: column;
}
.fui-waterfall__item-show {
opacity: 1;
}
.fui-waterfall__img-wrap {
background: #F1F4FA;
overflow: hidden;
}
.fui-waterfall__hidden {
overflow: hidden;
}
.fui-waterfall__img {
/* #ifndef APP-NVUE */
display: block;
/* #endif */
}
</style>

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