1.简单实现复制功能
navigator.clipboard.writeText(url)
2.实现简易蒙层
<template>
<div id="mask" ref="mask" v-if="passwordDialog">
<n-card class="absolute" style="width: 30vw;height: 25vh;top: 50%;left: 50%;transform: translate(-50%, -50%);">
<div>密码保护</div>
<n-divider></n-divider>
<n-input placeholder="访问密码" v-model:value="password" type="password">
</n-input>
<div class="flex justify-center my-6">
<!-- <n-button class="mx-3" style="margin:10px 20px 10px 10px" @click="goBack">退出</n-button> -->
<n-button class="mx-3" type="primary" @click="checkPassword" style="margin:10px 0px 10px 20px">确定</n-button>
</div>
</n-card>
</div>
</template>
<script>
import {onMounted,ref} from 'vue'
// import { useAuth } from '@/store/services/auth'
// import { usePublishStore } from "@/store/modules/publishStore/publishStore";
// import { useNotification } from 'naive-ui'
export default {
name: 'PasswordCheck',
setup() {
let mask=ref(null)
// const publishStore = usePublishStore();
// const auth = useAuth();
// const notification = useNotification();
return {
mask,
// auth,
// publishStore,
// notification,
};
},
props: {
publishId: {
type: String,
required: true,
},
experienceId: {
type: String,
required: true,
},
checkMode: {
type: String,
default: 'play',
},
passwordDialog:{
type: Boolean,
default: false,
}
},
data() {
return {
password: '',
};
},
methods: {
async checkPassword() {
// todo: call api
// let loginResponse = undefined;
// try {
// // loginResponse = await this.auth.authenticate({
// // strategy: 'publish',
// // publishId: this.publishId,
// // userInput: this.password,
// // });
// } catch (e) {
// // this.notification['error']({
// // content: '验证失败',
// // placement: 'top',
// // duration: 2500,
// // keepAliveOnHover: true
// // });
// return;
// }
// if (!loginResponse || loginResponse.error) {
// // this.notification['error']({
// // content: '验证失败',
// // placement: 'top',
// // duration: 2500,
// // keepAliveOnHover: true
// // });
// return;
// }
// this.notification['success']({
// content: '验证成功',
// placement: 'top',
// duration: 2500,
// keepAliveOnHover: true
// });
// this.verified = true;
// this.$emit('verified', true, this.checkMode);
this.$emit('checkPassword',false,this.password)
},
goBack(){
this.$emit('closePasswordDialog',false)
}
},
// onMounted(() => {
// // this.$refs.mask.style.height = window.document.getElementById('app').clientHeight + 'px'
// })
}
</script>
<style scoped>
#mask{
width: 100%;
height: 100%;
/* opacity: 0.8; */
background-color: rgba(240,240,240,1);
bottom: 0;
left: 0;
position: fixed;
z-index: 998;
}
.mask_img{
width: 316px;
height: 200px;
z-index: 999;
position: fixed;
right: 0px;
}
</style>
3.实现随机密码
// 随机生成密码
function randPassword(len?:number, passwordArr?:Array<string>) {
let length = len || 8;
// 密码串----默认 大写字母 小写字母 数字
let passwordArray = passwordArr || ['ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz', '1234567890'];
let password = '';
// 随机生成开始字符串
let startIndex:any = parseInt(Math.random() * (length));
let randIndex = [];
for (let i = 0; i < length; i++) {
// 创建数组,用于取随机位置 [0,1,2,3,4,5,....]
randIndex.push(i);
}
for (let i = 0; i < length; i++) {
// 根据随机数组生成随机位置
let r = parseInt(Math.random() * (randIndex.length));
let num = randIndex[r] + startIndex;
// 根据随机值取余数
let randRemainder = num % passwordArray.length;
// 当前密码串【大写字母,小写字母,数字等】
let currentPassword = passwordArray[randRemainder];
// 根据当前密码串长度取随机数
let index = parseInt(Math.random() * (currentPassword.length));
// 获取随机字符串
let str = currentPassword.substr(index, 1);
// 删除随机数组中已经使用的值
randIndex.splice(r, 1);
password += str;
}
return password;
}
4.实现弹窗显示的几种方式
(1)在父组件里使用子组件的时候使用v-if,可以实现初始化时候子组件不加载,触发才展示,初始化生命周期,
(2),在子组件里面控制弹框显隐,使用defineExpose向父组件暴露自己的方法
父组件页面使用ref获取子组件dom,调用暴露出来的子组件的方法
(3)子组件控制显影,使用父传子,子传父(笨老方法)
5.下载
const downloadExperience=(data:any)=> {
console.log('下载!!!',data);
const url = data&&data.GLB_file?data.GLB_file.objectStorageDownloadUrl:""
//此处返回的blob对象
// let fileName = url.slice(url.lastIndexOf('/') + 1);
let fileName = data&&data.GLB_file&&data.GLB_file.objectStorageDownloadUrl?data.GLB_file.name:"";
console.log('fileName',fileName);
var fileURL = window.URL.createObjectURL(new Blob([url]));
console.log('fileURL',fileURL)
var fileLink = document.createElement('a');
fileLink.href = fileURL;
fileLink.setAttribute('download',fileName);
document.body.appendChild(fileLink);
fileLink.click();
}
6,预览.csv文件(和excel文件类似,但是只能插入一个表,excel可以插入多个表)
// 预览
const preview=()=> {
console.log('preview',fileUrl.value);
let xhr = new XMLHttpRequest();
xhr.open("get", fileUrl.value, true);
xhr.responseType = "blob";
let that = getCurrentInstance();
console.log('nnnnnnnnnnnnnnnnnnnnnnnnn');
xhr.onload = function (e) {
if (xhr.status === 200) {
let fileReader = new FileReader();
fileReader.readAsArrayBuffer(xhr.response);
fileReader.onload = (res) => {
console.log('转换成功!');
let stream = res.target.result;
let workbook = XLSX.read(stream,{ type: 'binary'});//解析二进制格式数据
console.log('二进制数据的解析:')
console.log(workbook)
let worksheet = workbook.Sheets[workbook.SheetNames[0]];//获取第一个Sheet
const sheet2JSONOpts = {
defval: ''
}
let result = XLSX.utils.sheet_to_json(worksheet,sheet2JSONOpts);//json数据格式
console.log('worksheet',worksheet,'result',result);
workResult.value=result as any
previewBobShow.value=true;
};
}
};
xhr.send();
}
7.实现项目国际化
参考链接https://blog.csdn.net/qq_41809113/article/details/1266796
8.实现树结构的增删改查(以navie ui为例)
树结构组件
modifyGroup是新增弹框
<template>
<div class=" h-full">
<n-card class="tv-main h-full p-2">
<div class="title flex items-center" style="flex-wrap: nowrap;">
<n-button text>{{ $t('Team_list') }}</n-button>
<n-input class="ml-2" v-model:value="filterName" style="border-radius: 6px;"></n-input>
</div>
<div class="content">
<p class="mt-3 cursor-pointer" @click="getAll" :class="isAll ? 'text-green-500' : ''">{{ $t('All') }}</p>
<!-- <p
class="mt-2 cursor-pointer"
:class="t.id === currentTeam.id && !isAll ? 'text-green-500' : ''"
v-for="t in teamData"
:key="t.id"
@click="selectData(t)"
>
{{ t.name }}
</p> -->
<n-tree
draggable
block-line
key-field="id"
label-field="name"
:data="teamData"
expand-on-click
:checked-keys="checkedKeys"
:check-strategy="checkStrategy"
:allow-checking-not-loaded="cascade"
:cascade="cascade"
:expanded-keys="expandedKeys"
:on-load="handleLoad"
:node-props="nodeProps"
:render-suffix="renderSuffix"
@drop="handleDrop"
@update:checked-keys="handleCheckedKeysChange"
@update:expanded-keys="handleExpandedKeysChange"
/>
</div>
<div v-if="editTeamShow" @click="addTeam">
<n-icon>
<Add />
</n-icon>
<span>新增团队</span>
</div>
<template #footer>
<n-button class="edit-group-btn" v-if="!editTeamShow" @click="editGroup">编辑团队</n-button>
<div v-if="editTeamShow" style="display:flex; justify-content:space-between ;">
<n-button type="primary" class="operationBtn">保存</n-button>
<n-button ghost class="operationBtn" @click="editTeamShow=false">取消</n-button>
</div>
</template>
</n-card>
</div>
<ModifyGroup ref="editTeamRef" :editTeam="editTeam" :teamData="teamData" @ensure="ensure" @cancel="cancel"></ModifyGroup>
</template>
<script setup lang="ts">
import { useTeamsGroupsStore } from '@/store/modules/teamsStore/teamsGroupsStore'
import { EditGroup } from '@/views/chart/ContentEdit/components/EditGroup'
import { computed, onMounted, reactive, ref, watch,h,} from 'vue'
import {NButton,NIcon,NText,NInput} from 'naive-ui'
import ModifyGroup from './modifyGroup.vue'
import { icon } from '@/plugins'
import { Add } from '@vicons/ionicons5'
const teamsGroupsStore = useTeamsGroupsStore()
const {
TrashIcon
} = icon.ionicons5
const $t = window['$t']
const filterName = ref('')
const isAll = ref(true)
const editTeamRef=ref()
const checkedKeys = ref<string[]>([])
const checkStrategy = ref<'all' | 'parent' | 'child'>('all')
const expandedKeys = ref<string[]>([])
let editTeamShow=ref(false)
let dbclickShow =ref(false)
let editTeam=ref({})
let teamData = ref(
[
{
createdAt: "2023-06-14T17:17:02.720Z",
deletedAt: null,
description: "超级管理员团队",
id: "1",
name: "超级管理员团队",
parentId: null,
updatedAt: "2023-06-20T18:37:45.218Z",
childern:[
{
createdAt: "2023-06-14T17:17:02.720Z",
deletedAt: null,
description: "超级管理员团队1",
id: "24b38e21-17fa-4d79-a79a-7ed69e8d2255",
name: "超级管理员团队1",
parentId: '1',
updatedAt: "2023-06-20T18:37:45.218Z",
}
]
},
{
createdAt: "2023-06-14T17:17:02.720Z",
deletedAt: null,
description: "管理员团队",
id: "24b38e21-17fa-4d79-a79a-7ed69e8d2255",
name: "管理员团队",
parentId: null,
updatedAt: "2023-06-20T18:37:45.218Z",
}
]
)
const cascade = ref(true)
// const teamData = computed(() => {
// return teamsGroupsStore.allData
// })
const selectData = (data: any) => {
console.log('selectData',data);
isAll.value = false
teamsGroupsStore.setCurrentData(data)
}
const getAll = () => {
isAll.value = true
teamsGroupsStore.setCurrentData({})
teamData.value.length = 0
teamData.value.push(...teamsGroupsStore.allData)
}
const currentTeam = computed(() => {
return teamsGroupsStore.currentData
})
const editGroup=()=> {
editTeamShow.value=true
}
watch(
() => teamsGroupsStore.currentData,
val => {
if (val.id) {
isAll.value = false
} else {
isAll.value = true
}
},
{ immediate: true }
)
watch(
() => filterName.value,
async val => {
await teamsGroupsStore.loadData({
name: {
$like: '%' + filterName.value + '%'
}
})
teamData.value.length = 0
teamData.value.push(...teamsGroupsStore.filterData)
}
)
watch(
() => teamsGroupsStore.allData,
val => {
teamData.value.length = 0
teamData.value.push(...teamsGroupsStore.allData)
}
)
// watch(editTeamShow,(newValue,oldValue)=> {
// if(newValue){
// renderSuffix()
// }
// },{})
const renderSuffix=(info :any)=> {
if(editTeamShow.value){
return h(NIcon, {onClick: () => deleteGroupItem(info.option.id)}, { default: () => h(TrashIcon) })
}else {
return null
}
}
const handleDrop=()=>{
}
const nodeProps=(data :any)=> {
return {
ondblclick(){
if(editTeamShow.value){
editTeamRef.value.show('edit')
// dbclickShow.value=true
editTeam.value=data.option
}else {
editTeam.value=data.option={}
}
console.log('option',data.option,' editTeam.value', editTeam.value)
},
onClick(){
selectData(data.option)
}
}
}
const addTeam=()=> {
editTeamRef.value.show('add')
}
const ensure=async()=> {
// dbclickShow.value=false
await teamsGroupsStore.loadData()
}
const cancel=()=> {
// dbclickShow.value=false
}
const deleteGroupItem=async (data)=> {
console.log('删除',data);
const result = await teamsGroupsStore.remove(data)
console.log('deleteGroupItem',result);
await teamsGroupsStore.loadData()
}
const handleLoad = (node: TreeOption) => {
console.log(node, 'node')
// let params = {
// applicationId: node.applicationId,
// parentId: node.id
// }
// if (node.level > 0) {
// params.parentId = node.parentId
// }
// return commonStore.getCurrentToolChildren(params).then(res => {
// console.log('res',res,'params',params);
// const level = node['level'] + 1
// node.children = res.map(i => ({
// ...i,
// label: i.name,
// level,
// key: i.id,
// isLeaf: i.content && i.content.isLeaf === true ? true : false,
// parentId: level > 0 ? node.id : ''
// }))
// })
}
const handleCheckedKeysChange = (keys: string[]) => {
console.log(keys)
checkedKeys.value = keys
}
const handleExpandedKeysChange = (keys: string[]) => {
console.log(keys)
expandedKeys.value = keys
}
// 一维数组转换为树结构
const arrayToTree = (list) => {
let obj = {}
let res = []
for(let item of list){
obj[item.id] = item
}
console.log('obj',obj);
for(let item of list){
if(obj[item.parentId]){
// console.log('obj[item.parentId]',obj[item.parentId]);
(obj[item.parentId].children || (obj[item.parentId].children = [])).push(item)
let index = res.findIndex(e=>e.id=== obj[item.parentId].id)
if(index){
res.splice(index,1)
res.push(obj[item.parentId])
}
}else{
res.push(item)
}
}
return res
};
onMounted(async() => {
await teamsGroupsStore.loadData()
teamData.value.length = 0
const data= [...teamsGroupsStore.allData]
if (teamData.value.length) {
const data =teamData.value
selectData(data[0])
}
teamData.value=data
console.log('data',data,'teamsGroupsStore.allData',teamsGroupsStore.allData);
})
</script>
<style lang="scss" scoped>
@include TV(project) {
.content-top {
top: $--header-height;
margin-top: 1px;
}
}
:deep(.n-card > .n-card__content, .n-card > .n-card__footer){
/* padding: 0; */
}
</style>
注意!!!!(删除图标的点击方法必须写箭头函数,否则会出问题)
9.实现项目换皮肤
页面效果展示
10.接口超时
(演示的项目接口是用feathers传输数据的,可参考feathersjs的安装和使用-CSDN博客)
了解了之后接下来进入正题
获取数据处理之后,存在响应式数据里面,因为数据是响应式,为了优化效果,可以加些东西,比如我这里需要超时数据的是表单,那么我会在表单项下面加一行红字提示,一旦接受到数据,红字提示就会消失,因为数据是响应式,很简单的方法
11.外部excel文件实现字段匹配,导入到页面表格
先看例子效果
下面来展示如何实现该功能
外部菜单以及头部按钮组件,表格页面切换的文件
<template>
<div class="data-table">
<div class="header" v-if="props.tableinfo.showTitle">
<div class="title">
<img src="~img/right-arrow.png" alt="" class="mr-2" />
<span>{{ props.tableinfo.title }}</span>
</div>
<div class="menu">
<template v-for="item of props.tableinfo.menu">
<el-tooltip
v-if="item !== 'imports'"
class="box-item"
effect="dark"
:content="getContent(item)"
>
<el-button
type="primary"
:loading="loadingStatus[item]"
:icon="getIcon(item)"
@click="handleButtonClicked(item)"
></el-button>
</el-tooltip>
<el-tooltip v-else class="box-item" effect="dark" content="导入">
<el-button
type="primary"
:disabled="loadingStatus.imports"
:icon="FolderOpened"
@click="openExcelModal"
></el-button>
</el-tooltip>
</template>
</div>
</div>
<div class="content">
<router-view v-slot="{ Component }">
<keep-alive>
<component ref="content" :is="Component"></component>
</keep-alive>
</router-view>
</div>
</div>
<ExcelDataImportDialog
ref="excelDialog"
:tableHead="content?.columnList"
:selectItem="content?.activeType"
:title="props.tableinfo.title"
@update:value="refreshData"
></ExcelDataImportDialog>
</template>
<script setup lang="ts">
import {
CirclePlus,
Delete,
DocumentChecked,
FolderOpened,
Refresh,
Search,
Download,
} from '@element-plus/icons-vue'
import { watch, ref, reactive } from 'vue'
import { useRouter } from 'vue-router'
import ExcelDataImportDialog from './TableComponent/ExcelDataImportDialog.vue'
const router = useRouter()
const props = defineProps<{ tableinfo: DATAMANAGETREE }>()
const content = ref()
let excelDialog = ref<typeof ExcelDataImportDialog>()
const loadingStatus = reactive({
add: false,
del: false,
update: false,
search: false,
refresh: false,
imports: false,
exports: false,
})
watch(
() => props.tableinfo,
(value) => {
router.push('/datamanage/' + value.index)
},
{
immediate: true,
}
)
const getIcon = (type: MENUOPERATION) => {
switch (type) {
case 'add':
return CirclePlus
case 'del':
return Delete
case 'update':
return DocumentChecked
case 'search':
return Search
case 'refresh':
return Refresh
case 'exports':
return Download
}
}
const getContent = (type: MENUOPERATION) => {
switch (type) {
case 'add':
return '新增'
case 'del':
return '删除'
case 'update':
return '保存'
case 'search':
return '查询'
case 'refresh':
return '刷新'
case 'imports':
return '导入'
case 'exports':
return '导出'
}
}
const handleButtonClicked = async (type: MENUOPERATION, data?: OBJ | OBJ[]) => {
// console.log('handleButtonClicked',type);
try {
loadingStatus[type] = true
await content.value[type](data)
} catch (error) {
console.error('未暴露的操作: ', type)
console.error('error: ', error)
} finally {
loadingStatus[type] = false
}
}
const openExcelModal = () => {
excelDialog.value?.openExcelDialog()
}
const refreshData = () => {
if (props.tableinfo.title === '井眼轨迹') {
// console.log('xxxxxxxxxxxxxxxxxx', content.value.activeType);
if (content.value.activeType === '实钻轨迹') {
content.value.getDataList('Survey')
} else {
content.value.getDataList('Design')
}
} else if (props.tableinfo.title === '地层岩性') {
if (content.value.activeType === '实际岩性') {
content.value.getDataList('Survey')
} else {
content.value.getDataList('Design')
}
} else {
content.value.getDataList()
}
}
</script>
<style scoped lang="scss">
.data-table {
position: relative;
display: flex;
flex-direction: column;
margin: 0 0 0 2rem;
width: 100%;
height: 100%;
.header {
display: flex;
justify-content: space-between;
height: 2.1rem;
padding: 0px 0.6rem;
margin-bottom: 0.5rem;
.title {
display: flex;
align-items: center;
font-weight: 700;
color: white;
line-height: 1rem;
}
.menu {
display: flex;
justify-content: space-around;
}
}
.content {
height: 100%;
background: url('img/tablebg.png');
padding: 1rem 1.5rem 0.6rem 1.5rem;
background-size: 100% 100%;
background-repeat: no-repeat;
overflow-y: auto;
scrollbar-width: thin;
&::-webkit-scrollbar {
width: 3px;
background-color: rgb(5, 20, 41);
}
&::-webkit-scrollbar-thumb {
background-color: rgb(0, 46, 88);
}
.tablecontent {
justify-content: space-between;
:deep(.el-table) {
height: 100%;
background-color: transparent;
}
}
}
}
</style>
<style lang='scss'>
:deep(.cell p) {
height: 32px;
margin: 10px 0;
display: flex;
align-items: center;
justify-content: center;
}
.el-radio-button {
--el-radio-button-checked-bg-color: rgb(1, 87, 165);
--el-radio-button-checked-border-color: rgb(1, 87, 165);
.el-radio-button__inner {
color: white;
background: rgb(1, 34, 65);
border: none;
}
}
.el-radio-button:first-child .el-radio-button__inner {
border-left: none !important;
}
</style>
表格页面(地层信息页面)
<template>
<div class="tablecontent relative" ref="tableRef">
<el-table
class="w-full"
:data="tableData"
v-loading="loading"
highlight-current-row
>
<el-table-column
v-for="item in columnList"
:prop="item.prop"
:label="item.label"
:key="item.prop"
align="center"
>
<template #default="scope">
<p>
<InputSpan
v-model:value="scope.row[item.prop]"
:enable-input="true"
style="height: 100%"
/>
</p>
</template>
</el-table-column>
</el-table>
<div v-if="addData">
<div class="inline-flex m-2 w-full">
<el-input
class="mx-6"
v-for="(_value, key) in addDataList"
v-model="addDataList[key]"
:autofocus="true"
:key="key"
size="large"
text
/>
</div>
<div class="inline-flex mt-2 w-full justify-center">
<el-button type="primary" @click="handleSumit">确定</el-button>
<el-button type="primary" @click="handleExit">取消</el-button>
</div>
</div>
<div class="mt-1">
<el-pagination
small
background
layout="prev, pager, next,"
:total="total"
:current-page="currentPage"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
hide-on-single-page
v-model:page-size="pageSize"
/>
</div>
</div>
</template>
<script lang="ts" setup>
import {onMounted, ref} from 'vue'
import useStore from '@/store'
import InputSpan from '../../components/InputSpan/index.vue'
import {POST} from '@/utils/request.ts'
import {exportXLSX} from '@/utils'
import {reactive} from 'vue'
const appStore = useStore().AppStore
let tableData = ref<GEOLOGYWELLDATA[]>([])
let total = ref(0)
let pageSize = ref<number>(10)
let currentPage = ref<number>(1)
let loading = ref<boolean>(false)
let addData = ref<boolean>(false)
let tableRef = ref(null)
const columnList = [
{prop: 'tvd', label: '井深'},
{prop: 'dtc', label: '纵波时差'},
{prop: 'formationDensity', label: '岩石密度'},
{prop: 'gammaRay', label: 'GR'},
{prop: 'fluidDensity', label: '钻井液密度'},
{prop: 'cnl', label: 'CNL'},
]
const addDataList = reactive({
tvd: 0,
dtc: 0,
formationDensity: 0,
gammaRay: 0,
fluidDensity: 0,
cnl: 0,
})
const getDataList = async (force: boolean = false) => {
loading.value = true
currentPage.value = 1
return appStore
.loadGeologyWellData(force)
.then((data) => {
// console.log('ddddddddddddddddddddd',data);
calculatePageSize()
total.value = data.length
})
.finally(() => {
loading.value = false
})
}
const calculatePageSize = () => {
const table = document.querySelector('.el-table') as HTMLElement
const tableHeader = document.querySelector('.el-table__header') as HTMLElement
if (table && table.clientHeight) {
let number = Math.floor((table.clientHeight - tableHeader.clientHeight) / 76)
handleSizeChange(number)
}
}
const handleCurrentChange = (index: number) => {
if (!appStore.geologyWellData) {
return
}
currentPage.value = index
tableData.value = appStore.geologyWellData.slice(
(currentPage.value - 1) * pageSize.value,
pageSize.value * index
)
}
const handleSizeChange = (index: number) => {
if (!appStore.geologyWellData) {
return
}
pageSize.value = index
tableData.value = appStore.geologyWellData.slice(
(currentPage.value - 1) * pageSize.value,
pageSize.value * currentPage.value
)
}
const handleSumit = () => {
if (!appStore.geologyWellData) {
return
}
appStore.geologyWellData.push({
...addDataList,
wellboreID: appStore.wellboreID,
} as GEOLOGYWELLDATA)
tableData.value = appStore.geologyWellData.slice(
(currentPage.value - 1) * pageSize.value,
pageSize.value * currentPage.value
)
total.value = appStore.geologyWellData.length
addData.value = false
// update().then(() => (addData.value = false))
}
const handleExit = () => {
addData.value = false
}
// 添加
const add = async () => {
addData.value = !addData.value
}
// 刷新
const refresh = async () => {
return getDataList(true)
}
// 导入
const imports = async (excel: OBJ[]) => {
const array: GEOLOGYWELLDATA[] = []
excel.forEach((item) => {
const obj = {
wellboreID: appStore.wellboreID,
tvd: item['井深'],
dtc: item['纵波时差'],
formationDensity: item['岩石密度'],
gammaRay: item['GR'],
fluidDensity: item['钻井液密度'],
cnl: item['CNL'],
}
array.push(obj as GEOLOGYWELLDATA)
})
appStore.geologyWellData = array
tableData.value = array.slice(0, pageSize.value)
total.value = array.length
return
}
// 保存
const update = async () => {
return POST(
appStore.baseURL + 'geology-well-log?wellboreID=' + appStore.wellboreID,
appStore.geologyWellData
).then((res) => {
ElMessage.success(res)
})
}
//导出
const exports = async () => {
if (!appStore.geologyWellData) {
return
}
return exportXLSX(appStore.geologyWellData, columnList, '地层信息')
}
onMounted(() => {
getDataList()
window.addEventListener('resize', calculatePageSize)
})
defineExpose({
add,
refresh,
imports,
update,
exports,
getDataList,
columnList,
})
</script>
<style lang="scss" scoped>
.tablecontent {
display: flex;
flex-direction: column;
height: 100%;
}
</style>
导入弹框页面
<template>
<el-dialog
v-model="dialogVisible"
class="myDialog"
title="Excel数据导入"
width="50%"
:before-close="handleClose"
>
<el-form :model="form">
<el-form-item label="请选择需要导入的Excel文件:">
<!-- <el-input v-model="form.importFileName" /> -->
<el-upload
action=""
accept="'.xlsx','.xls'"
:on-change="handleChange"
:auto-upload="false"
:file-list="fileList"
:show-file-list="false"
>
<el-button size="small" type="primary" style="margin-left:10px;">
<template #icon>
<img src="../../../../assets/icon/uploadImg.png" alt="" class="uploadImg"/>
</template>
上传
</el-button>
</el-upload>
</el-form-item>
</el-form>
<div class="matchFields">
<div>
<span class="choiceTitle">文件字段</span>
<el-card class="box-card">
<el-scrollbar height="240px">
<!-- <div v-for="o in 10" :key="o" class="text item">{{ o }}</div> -->
<el-radio-group v-model="choseFileFiled">
<el-radio v-for="o in fileField" :key="o" :label="o" size="large"></el-radio>
</el-radio-group>
</el-scrollbar>
</el-card>
</div>
<div class="addBox">
<img src="../../../../assets/icon/add.png" alt="" class="addImg"/>
</div>
<div>
<span class="choiceTitle">数据字段</span>
<el-card class="box-card">
<el-scrollbar height="240px">
<el-radio-group v-model="choseDataFiled">
<el-radio
v-for="o in dataField"
:key="o"
:label="o"
size="large"
></el-radio>
</el-radio-group>
</el-scrollbar>
</el-card>
</div>
<div class="mateButtons">
<img src="../../../../assets/icon/arrow.png" alt="" class="arrowImg"/>
<el-button type="primary" @click="matchFields" class="match">匹配</el-button>
<el-button type="primary" style="margin: 10px 0" @click="remove" class="remove"
>移除</el-button
>
</div>
<div class="mateContent">
<span class="choiceTitle">匹配结果</span>
<!-- <div> -->
<el-card class="mateTableCard">
<el-scrollbar height="240px">
<div class="mateTableHead">
<div class="mateTableColLeft">文件字段</div>
<div class="mateTableColRight">数据字段</div>
</div>
<div
v-for="(item, index) in matchTable"
:key="index"
class="mateTableRow"
@click="choseDataRemove(index)"
:style="{background:choseStyle===index?'rgba(10, 40, 82, 1)':''}"
>
<div class="mateTableColFile">{{ item.file }}</div>
<div class="mateTableColData">{{ item.data }}</div>
</div>
</el-scrollbar>
<!-- </div> -->
</el-card>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<!-- <el-button @click="closeExcelDialog">返回</el-button> -->
<el-button type="primary" @click="importExcelData">导入</el-button>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { onMounted, ref, reactive ,watch} from 'vue'
import useStore from '@/store'
import * as XLSX from 'xlsx'
import { storeToRefs } from 'pinia'
import InputSpan from '../../components/InputSpan/index.vue'
import { POST } from '@/utils/request.ts'
import { exportXLSX } from '@/utils'
const props= defineProps({
tableHead:Object,
title:String,
selectItem: String
})
const emits = defineEmits(['update:value'])
const appStore = useStore().AppStore
let { geologyWellData,pressureData,trackData ,designTrackData,wellStructureData,stratumData,designStratumData,engineerFluidData} = storeToRefs(appStore)
let dialogVisible = ref(false)
let fileList = ref([])
let dataField = ref([])
let fileField = ref([])
let choseStyle=ref(null)
const form = reactive({
importFileName: '',
})
// let checked1=ref(false)
let choseDataFiled = ref(null)
let choseFileFiled = ref(null)
let matchTable = ref([])
let deleteIndex = ref(null)
let excelContent = ref([])
const handleClose = () => {
closeExcelDialog()
// dialogVisible.value = false
}
const openExcelDialog = () => {
dialogVisible.value = true
}
const closeExcelDialog = () => {
dialogVisible.value = false
// fileField.value=[]
// form.importFileName=''
// matchTable.value=[]
deleteIndex.value = null
reset()
}
const reset =()=> {
// 置空
fileField.value=[]
let arr=[]
let dealData = []
props.tableHead.forEach(e=> {
if(e.children){
dealData.push(e.children)
}else {
dealData.push(e)
}
arr=dealData.flat().map(e=>e.label)
})
if(!arr.length){
arr=props.tableHead.map(e=>e.label)
}
dataField.value=arr
matchTable.value=[]
choseFileFiled.value=null
choseDataFiled.value=null
fileList.value=[]
}
const getObjKeys=(key)=> {
return matchTable.value.find(e=> e.data===key)?.file
}
const importExcelData = () => {
let arr=[]
let dealData = []
props.tableHead.forEach(e=> {
if(e.children){
dealData.push(e.children)
}else {
dealData.push(e)
}
arr=dealData.flat().map(e=>e.label)
})
// console.log('props.tableHead',props.tableHead,arr);
if (props.title!=='钻井液性能'&&matchTable.value.length !== props.tableHead.length) {
ElMessage({
message: '匹配未完成',
type: 'warning',
})
}else if(arr.length!==0&&arr.length !== matchTable.value.length){
ElMessage({
message: '匹配未完成',
type: 'warning',
})
}else{
let data = []
if(props.title==='地层信息'){
const key=getObjKeys('井深')
const keyOne=getObjKeys('纵波时差')
const keyTwo=getObjKeys('岩石密度')
const keyThree=getObjKeys('GR')
const keyFour=getObjKeys('钻井液密度')
const keyFive=getObjKeys('CNL')
data = excelContent.value.map((item,index) => {
return {
wellboreID: appStore.wellboreID,
tvd: item[key],
dtc: item[keyOne],
formationDensity: item[keyTwo],
gammaRay: item[keyThree],
fluidDensity: item[keyFour],
cnl: item[keyFive],
}
});
geologyWellData.value=data
}else if(props.title==='地层压力'){
const key=getObjKeys('垂深(m)')
const keyOne=getObjKeys('孔隙压力(g/cm3)')
const keyTwo=getObjKeys('破裂压力(g/cm3)')
const keyThree=getObjKeys('坍塌压力(g/cm3)')
data = excelContent.value.map((item,index) => {
return {
wellboreID: appStore.wellboreID,
tvd: item[key],
porePressure: item[keyOne],
fracturePressure: item[keyTwo],
collapsePressure: item[keyThree],
}
});
pressureData.value=data
}else if(props.title==='钻井液性能'){
const key=getObjKeys('密度g/cm3')
const keyOne=getObjKeys('漏斗粘度s')
const keyTwo=getObjKeys('3转')
const keyThree=getObjKeys('6转')
const keyFour=getObjKeys('100转')
const keyFive=getObjKeys('200转')
const keySix=getObjKeys('300转')
const keySeven=getObjKeys('600转')
const keyEight=getObjKeys('失水mm')
const keyNine=getObjKeys('日期')
data = excelContent.value.map((item,index) => {
return {
wellboreID: appStore.wellboreID,
fluidDensity: item[key],
funnelVisc: item[keyOne],
r3: item[keyTwo],
r6: item[keyThree],
r100: item[keyFour],
r200: item[keyFive],
r300: item[keySix],
r600: item[keySeven],
waterLoss: item[keyEight],
testDatetime: item[keyNine],
}
});
engineerFluidData.value=data
}else if(props.title==='井眼轨迹'){
const key=getObjKeys('井深 (m)')
const keyOne=getObjKeys('井斜角 (°)')
const keyTwo=getObjKeys('方位角 (°)')
data = excelContent.value.map((item,index) => {
return {
wellboreID: appStore.wellboreID,
md: item[key],
inc: item[keyOne],
azi: item[keyTwo],
}
});
if(props.selectItem&&props.selectItem==='实钻轨迹'){
trackData.value=data
}else {
designTrackData.value=data
}
}else if(props.title==='井身结构'){
const key=getObjKeys('名称')
const keyOne=getObjKeys('井深(mm)')
const keyTwo=getObjKeys('套管顶深(m)')
const keyThree=getObjKeys('套管下深(m)')
const keyFour=getObjKeys('套管外径(m)')
const keyFive=getObjKeys('套管内径(m)')
const keySix=getObjKeys('水泥环顶深(m)')
const keySeven=getObjKeys('水泥环底深(m)')
data = excelContent.value.map((item,index) => {
return {
wellboreID: appStore.wellboreID,
casingName: item[key],
wellboreSize: item[keyOne],
casingTopDepth: item[keyTwo],
casingSetDepth: item[keyThree],
casingOuterD: item[keyFour],
casingInnerD: item[keyFive],
cementStartDepth: item[keySix],
cementEndDepth: item[keySeven],
}
});
wellStructureData.value=data
}else if(props.title==='地层岩性'){
const key=getObjKeys('岩层名称')
const keyOne=getObjKeys('顶深')
const keyTwo=getObjKeys('底深')
const keyThree=getObjKeys('厚度')
const keyFour=getObjKeys('岩性')
data = excelContent.value.map((item,index) => {
return {
wellboreID: appStore.wellboreID,
formationName: item[key],
topDepth: item[keyOne],
bottomDepth: item[keyTwo],
thickness: item[keyThree],
lithology: item[keyFour],
recordType: props.selectItem,
}
});
if(props.selectItem&&props.selectItem==='实际岩性'){
stratumData.value=data
}else {
designStratumData.value=data
}
}
// console.log('55555555555551111111111111','matchTable',matchTable.value,'data',data);
emits('update:value', data)
dialogVisible.value = false
reset()
}
}
const handleChange = (file, fileLists) => {
// console.log(file)
// console.log(fileLists, document.getElementsByClassName('el-upload__input'))
// // 本地服务器路径
// console.log(URL.createObjectURL(file.raw))
// // 本地电脑路径
// console.log(document.getElementsByClassName('el-upload__input')[0].value)
handleExcel(file)
// const computedUrl =
// document.getElementsByClassName('el-upload__input')[0].value
// form.importFileName = computedUrl
}
const handleExcel = (files: UploadFile) => {
let file = files.raw
if (!file) {
return
} else if (!/\.(xls|xlsx)$/.test(file.name.toLowerCase())) {
ElMessage.error('上传格式不正确,请上传xls或者xlsx格式')
return
}
const fileReader = new FileReader()
fileReader.onload = (event: ProgressEvent<FileReader>) => {
try {
const data = event.target?.result
const workbook = XLSX.read(data, {
// 以字符编码的方式解析
type: 'binary',
})
// 取第一张表
const exlname = workbook.SheetNames[0]
const excel = XLSX.utils.sheet_to_json(workbook.Sheets[exlname]) // 生成json表格内容
excelContent.value = excel
// fileField.value=Object.keys(excel[0])
// console.log('cccccccccccccccccc',excel);
let max=0
excel.forEach(e=> {
const data = Object.keys(e)
max=Math.max(data.length,max)
})
excel.forEach(k=> {
if(Object.keys(k).length===max){
fileField.value=Object.keys(k)
}
})
// console.log('mmmmmmmmmm',max, fileField.value);
fileField.value.forEach((e,indexE) => {
dataField.value.forEach((f,indexF)=> {
if(e===f){
matchTable.value.push({file:e,data:f})
}
})
});
matchTable.value.forEach((i,index)=>{
fileField.value.forEach((j,index1)=> {
if(i.file===j){
fileField.value.splice(index1,1)
}
})
})
matchTable.value.forEach((i,index)=>{
dataField.value.forEach((j,index1)=> {
if(i.data===j){
dataField.value.splice(index1,1)
}
})
})
// 将 JSON 数据挂到 data 里
// handleButtonClicked('imports', excel)
} catch (error) {
ElMessage.error(String(error))
console.error('解析文件失败: ', error)
}
}
fileReader.readAsBinaryString(file)
}
// 匹配字段
const matchFields=()=> {
// console.log('matchFields',choseDataFiled.value,choseFileFiled.value);
if(choseDataFiled.value&&choseFileFiled.value){
matchTable.value.push({file:choseFileFiled.value,data:choseDataFiled.value})
const fileIndex = fileField.value.findIndex(e=>e=== choseFileFiled.value)
fileField.value.splice(fileIndex,1)
const dataIndex = dataField.value.findIndex(e=>e=== choseDataFiled.value)
dataField.value.splice(dataIndex,1)
}
}
// 移除
const remove=()=> {
if(deleteIndex.value || deleteIndex.value===0){
const deleteItem = matchTable.value.splice(deleteIndex.value,1)
fileField.value.push(deleteItem[0].file)
dataField.value.push(deleteItem[0].data)
choseStyle.value=null
}
}
const choseDataRemove = (i) => {
deleteIndex.value = i
choseStyle.value=i
}
const getDataList = async (force: boolean = false) => {
// loading.value = true
// currentPage.value = 1
return appStore.loadGeologyWellData(force).then((data) => {
dataField.value = Object.keys(data[0])
// tableData.value = data.slice(0, pageSize.value)
// total = data.length
})
}
watch(()=>props.tableHead,(newValue,oldValue)=> {
if(newValue&&newValue.length>0){
let arr=[]
let dealData = []
newValue.forEach(e=> {
if(e.children){
dealData.push(e.children)
}else{
dealData.push(e)
}
arr=dealData.flat().map(e=>e.label)
})
if(!arr.length){
arr=newValue.map(e=>e.label)
}
// console.log('xxxxxxxxxxxxxxxxxx',newValue,arr);
dataField.value=arr
}
},{
deep:true,
immediate:true
})
onMounted(() => {
// getDataList()
})
defineExpose({
openExcelDialog,
closeExcelDialog,
})
</script>
<style lang="scss" scoped>
.myDialog{
background-color:red!important;
}
.matchFields {
width: 100%;
/* height: 300px; */
display: flex;
/* justify-content: space-between; */
.choiceTitle{
font-size:14px;
color:#fff;
display:inline-block;
margin-bottom:8px;
}
.mateTableCard{
border: 1px solid rgba(10, 40, 82, 1);
border-top:none;
background: linear-gradient(0deg, #030D1B, #030D1B),linear-gradient(0deg, #0A2852, #0A2852);
}
.text {
font-size: 14px;
}
.item {
padding: 0 0 5px;
}
.box-card {
width: 150px;
}
.mateContent {
/* width: 300px; */
height: 300px;
/* background: #fff; */
flex:1;
:deep(.el-card__body){
padding:0;
}
}
.mateButtons {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin: auto 30px;
.arrowImg{
width: 38px;
height: 32px;
margin-bottom:13px;
}
.match, .remove{
width: 60px;
height: 32px;
padding:16px;
border-radius: 6px;
border: 1px solid rgba(16, 66, 137, 1);
background: linear-gradient(0deg, #061A37, #061A37),linear-gradient(0deg, #104289, #104289);
font-size:14px;
color:#fff;
}
}
.mateTableRow {
width: 100%;
height: 50px;
/* border: 1px solid; */
}
.mateTableRow:hover{
background:rgba(10, 40, 82, 1)
}
.mateTableHead{
width: 100%;
height: 50px;
background: linear-gradient(0deg, #061A37, #061A37),linear-gradient(0deg, #0A2852, #0A2852);
}
.mateTableColLeft {
border: 1px solid rgba(10, 40, 82, 1);
border-left: none;
}
.mateTableColRight {
border:1px solid rgba(10, 40, 82, 1);
border-left: none;
border-right:none;
}
.mateTableColLeft,
.mateTableColRight,
.mateTableColFile,
.mateTableColData
{
display: inline-block;
width: 50%;
height: 50px;
line-height: 50px;
text-align: center;
//styleName: Base/Strong;
font-family: SF Pro Text;
font-size: 14px;
color: rgba(255, 255, 255, 0.85);
}
.mateTableColFile,.mateTableColData{
border-right:1px solid rgba(10, 40, 82, 1);
border-bottom:1px solid rgba(10, 40, 82, 1);
}
.addBox{
width:100px;
height:300px;
display:flex;
justify-content:center;
align-items:center;
.addImg {
width:32px;
height:32px;
background-size:100%;
}
}
}
:deep(.el-form-item__content) {
flex-wrap: nowrap;
}
:deep(.el-card__body) {
padding: 10px;
}
:deep(.el-radio-group) {
margin-left: 0px;
}
:deep(.el-radio.el-radio--large) {
width: 100%;
}
:deep(.el-card__body){
/* border: 1px solid rgba(13, 53, 110, 1); */
background: linear-gradient(0deg, #030D1B, #030D1B),linear-gradient(0deg, #0D356E, #0D356E);
/* width: Hug (240px)
height: Fixed (270px) */
/* border-radius: 8px; */
}
:deep(.el-card){
border: 1px solid rgba(13, 53, 110, 1);
border-radius: 8px;
}
</style>
<style lang="scss">
.myDialog {
background:rgba(5, 20, 41, 1)!important;
.el-dialog__header{
width:100%;
height:64px;
/* line-height:64px; */
font-family: OPPOSans;
font-size: 18px;
border-bottom: 1px solid rgba(27, 50, 83, 1);
margin-right:0;
padding:20px;
}
.el-dialog__body{
/* padding: 20px 0 20px 24px; */
padding-top:20px;
padding-bottom: 20px;
}
.el-form-item__label {
color : rgba(255, 255, 255, 1);
}
.el-button--small{
width: 84px;
height: 32px;
padding: 0px, 16px, 0px, 16px;
border-radius: 6px;
background: linear-gradient(0deg, #061A37, #061A37),linear-gradient(0deg, #104289, #104289);
border: 1px solid rgba(16, 66, 137, 1);
//styleName: Base/Normal;
font-family: SF Pro Text;
font-size: 14px;
}
.el-dialog__footer {
border-top: 1px solid rgba(27, 50, 83, 1)
}
}
</style>
12.实现树节点的单选,以及将选中的树节点转化为新的树
页面效果如下:
实现代码如下:
两个树结构页面
<template>
<div class="flex h-full">
<div class="w-1/5 tree">
<div>
<div class="flex items-center title">展示井列表</div>
<el-tree
class="h-full"
:data="treeData"
:props="treeProps"
show-checkbox
node-key="uuid"
ref="treeRef"
@check="handleCurrentChange"
v-loading="!appStore.fullWellInfoData"
:default-expanded-keys="defaultExpandedKeys"
:default-checked-keys="defaultCheckedKeys"
/>
</div>
</div>
<div class="w-4/5 text-white">
<!-- <p>{{ curObj.DisplayField }}</p>
<ul>
<li v-for="item in wellList">{{ item.label ? item.label : item }}</li>
</ul> -->
<el-tree
class="h-full"
:data="choseTree"
:props="treeProps"
node-key="label"
v-loading="choseTreeStatus"
/>
<!-- <el-button @click="handleSave">保存</el-button> -->
</div>
</div>
</template>
<script lang="ts" setup>
import useStore from '@/store'
import { POST, GET } from '@/utils/request'
import { onMounted, ref, watch } from 'vue'
import { getValue } from '@/utils'
const appStore = useStore().AppStore
const treeRef = ref(null)
interface obj {
UsrName: string
DisplayWellboreList: string
CurrentWellboreID: string
DisplayField: string
CurrentDisplayParas: string
}
const wellList = ref([])
const defaultCheckedKeys = ref([])
let defaultExpandedKeys= ref([])
let choseTree = ref([])
let choseTreeStatus= ref(true)
const treeData = ref<FULLWELLINFODATA[]>()
const curObj = ref<obj>({
UsrName: '',
DisplayWellboreList: '',
CurrentWellboreID: '',
DisplayField: '',
CurrentDisplayParas: '',
})
// "UsrName": "test1",
// "DisplayWellboreList": "DQ8R9-H8-1,DQ8R9-H9-1",
// "CurrentWellboreID": "DQ8R9-H8-1",
// "DisplayField": "北航DQ油田",
// "CurrentDisplayParas": "MD, ROP, BitLocation, WOB, RotatryRPM, SPP, FlowrateIn"
// 表props
const treeProps = {
children: 'children',
label: 'label',
disabled: 'disabled',
}
onMounted(async () => {
await appStore.loadFulWellInfoData()
treeData.value = appStore.fullWellInfoData
console.log('tree!!!!!!!!!!!!!!!',treeData.value,treeData.value[0],treeData.value[0].$treeNodeId)
await getList()
})
const findRoot = (currentData:any) => {
return !currentData.parent ? currentData : findRoot(currentData.parent)
}
const deepMap = (selectedList:any, currentTreeData:any) => {
let final = null
for (let i = 0; i < currentTreeData.length; i++) {
const item = currentTreeData[i]
if (selectedList.some((dataItem:any) => {
// return dataItem === item.label
return dataItem === item.uuid
})) {
//匹配到当前父级
if (item.parent) {
//如果当前不是根节点,则继续往上找根节点
final = findRoot(item.parent)
} else {
// 匹配到根节点
final = item
}
} else if (item.children) {
//否则继续往下匹配
final = deepMap(selectedList, item.children)
}
if (final) {
//一旦匹配上,即可终止循环
break
}
}
return final
}
const deepFilter = ((currentObj:any, selectedList:any) => {
console.log('ffffffffffffffffffff',currentObj,selectedList);
currentObj.children = currentObj.children.filter((item:any) => {
console.log('xxxxxx',item);
if (selectedList.some((dataItem:any) => {
// return dataItem === item.label
return dataItem === item.uuid
})) {
console.log('123123123');
return item
} else if (item.children){
console.log('90909090');
item = deepFilter(item, selectedList)
if (item.children.length) return item
}
})
return currentObj
})
const deepChildren = (childrenList:any) => {
let list = []
childrenList.forEach((item:any) => {
let json = {
label: item.label,
uuid:item.uuid,
disabled: item.disabled,
type: item.type,
}
if (item.children){
json.children = []
json.children.push(...deepChildren(item.children))
list.push(json)
} else {
list.push(json)
}
})
return list
}
const cloneTree = (currentObj:any) => {
let copyData = {
label: currentObj.label,
uuid:currentObj.uuid,
disabled: currentObj.disabled,
type:currentObj.type,
children: []
}
copyData.children = deepChildren(currentObj.children)
return copyData
}
const getSelectedTreeData = (data:any) => {
choseTree.value = []
if (treeData.value) {
let obj = deepMap(data, treeData.value)
let filterObj = null
if (!obj) return
let copyObj = cloneTree(obj)
if (copyObj) filterObj = deepFilter(copyObj, data)
if (filterObj) choseTree.value.push(filterObj)
}
}
watch(
() => treeRef.value?.getCheckedKeys(),
// ()=>treeRef.value?.getCheckedNodes(),
(newData:any) => {
console.log('newData', newData)
getSelectedTreeData(newData)
},
{
immediate: true,
deep: true
}
)
const changeTreeData = (data) => {
data?.forEach((ele) => {
if (ele.type === 'Field') {
ele.disabled = true
} else {
ele.disabled = false
}
if (ele.children?.length) {
changeTreeData(ele.children)
}
})
}
// 处理后端返回的选中的子节点
let dealChildrenData = (tree)=> {
let temp = []
let dfs = (tree) => {
for (let i = 0; i < tree.length; i++) {
// temp.push(tree[i].label)
temp.push(tree[i].uuid)
let item = tree[i]
if (item.children) {
dfs(item.children)
}
}
}
dfs(tree)
return temp
}
const getList = () => {
GET(appStore.baseURL + 'usr-wellbore-display?UsrName=root').then((res) => {
let data = res.DisplayWellboreList.split(',')
wellList.value=data.filter(e=>e!=='')
// wellList.value = res.DisplayWellboreList.split(',')
let result = treeData.value.filter(e=> {
// if(e.label==res.DisplayField){
// return e
// }
if(e.name==res.DisplayField){
return e
}
})
let selectKeys=dealChildrenData(result)
// treeRef.value.setCheckedKeys(wellList.value)
console.log('xxxxxxxxxxxxxxxxxxx',res,result,selectKeys);
treeRef.value.setCheckedKeys(selectKeys)
defaultExpandedKeys.value = selectKeys
curObj.value = res
choseTree.value=result
choseTreeStatus.value=false
// defaultCheckedKeys.value.push(curObj.value.DisplayField)
changeTreeData(treeData.value)
})
}
// const handleNodeClick = (data: FULLWELLINFODATA) => {
// console.log('handleNodeClick ccccccccccccccccccc',data);
// // const info = data.info
// // switch (data.type) {
// // case 'Wellbore':
// // curObj.value.DisplayField = getValue(data.parent?.parent?.parent, 'label')
// // }
// // if (!info) {
// // return
// // }
// }
function arrayToTree(list, root) {
const result = [] // 用于存放结果
const map = {} // 用于存放 list 下的节点
// 1. 遍历 list,将 list 下的所有节点以 id 作为索引存入 map
for (const item of list) {
map[item.label] = { ...item } // 浅拷贝
}
// 2. 再次遍历,将根节点放入最外层,子节点放入父节点
for (const item of list) {
// 3. 获取节点的 id 和 父 id
const { id, parent_id,children,parent } = item // ES6 解构赋值
// 4. 如果是根节点,存入 result
if (item.parent === root) {
result.push(map[item.label])
} else {
// 5. 反之,存入到父节点
map[parent].children
? map[parent_id].children.push(map[id])
: (map[parent_id].children = [map[id]])
}
}
// 将结果返回
return result
}
// 函数拷贝
const copyObj = (obj = {}) => {
let newObj = null
// 判断是否需要继续进行递归
if(typeof obj == 'object' && obj !== null){
newObj = obj instanceof Array?[]:{}
// 进行下一层递归克隆
for(let i in obj){
newObj[i] = copyObj(obj[i])
}
}else{
newObj = obj
}
return newObj
}
// function dealRightTree(result,blockName,wellName,wellBoreName){
// // let result=JSON.parse(JSON.stringify(data))
// console.log('00000000000000000',result,blockName,wellName,wellBoreName);
// for(let i in result){
// let item = result[i]
// console.log('item/',item);
// if(item.type==='Block'){
// if(item.label!==blockName){
// result.splice(i,1)
// }
// }else if(item.type==='Well'){
// console.log('well;;;;;;;;;;;;',item.label,wellName);
// // debugger
// if(item.label!==wellName){
// result.splice(i,1)
// }
// }else if(item.type==='Wellbore'){
// if(item.label!==wellBoreName){
// result.splice(i,1)
// }
// }
// dealRightTree(item.children)
// }
// return result
// }
const handleCurrentChange = (data, node) => {
console.log('handleCurrentChange',data,node);
// let type = data.type
// let field: string
// let fieldParent :string
// if (type === 'Wellbore') {
// field = getValue(data.parent?.parent, 'label')
// fieldParent = getValue(data.parent?.parent.parent, 'label')
// } else if (type === 'Block') {
// field = getValue(data, 'label')
// fieldParent = getValue(data.parent, 'label')
// } else if (type === 'Well') {
// field = getValue(data.parent, 'label')
// fieldParent = getValue(data.parent?.parent, 'label')
// }
node.checkedNodes.push(...node.halfCheckedNodes)
let arr = node.checkedNodes.filter((ele) => {
return ele.type === 'Block'
})
if(arr.length>1){
ElMessage.warning('只可选择一个区块')
treeRef.value.setChecked(data.uuid, false,true)
return
}
return
// if(arr.length>1){
// ElMessage.warning('只可选择一个区块')
// treeRef.value.setChecked(data.label, false,true)
// }else{
// curObj.value.DisplayField=fieldParent
// let data = treeRef.value.getCheckedNodes()
// let select=null
// let findData = null
// let res = null
// console.log('data-------------',data);
// if(data.find(e=>e.type==='Block')){
// findData=data.find(e=>e.type==='Block')
// select = getValue(findData.parent, 'label')
// let blockData = findData.label
// // choseTree.value=treeData.value.filter(e=>e.label===select)
// }else if(data.find(e=>e.type==='Well')){
// findData= data.find(e=>e.type==='Well')
// select = getValue(findData.parent?.parent, 'label')
// let blockData = getValue(findData.parent, 'label')
// let wellBoreName =null
// // let yy = copyObj(treeData)
// let result =treeData.value.filter(e=>e.label===select)
// // debugger
// // let cloneData = JSON.parse(JSON.stringify(result))
// function dealRightTree(result){
// // let result=JSON.parse(JSON.stringify(data))
// for(let i in result){
// let item = result[i]
// console.log('item/',item);
// if(item.type==='Block'){
// if(item.label!==blockData){
// result.splice(i,1)
// }
// }else if(item.type==='Well'){
// console.log('well;;;;;;;;;;;;',item.label,findData.label);
// // debugger
// if(item.label!==findData.label){
// result.splice(i,1)
// }
// }else if(item.type==='Wellbore'&&wellBoreName){
// if(item.label!==wellBoreName){
// result.splice(i,1)
// }
// }
// dealRightTree(item.children)
// }
// return result
// }
// console.log(',,,,,,,,,,,,,',result,treeData,blockData,findData.label);
// // dealRightTree(result)
// // console.log('----------------',result,treeData);
// // choseTree.value=mmm(result)
// }else if(data.find(e=>e.type==='Wellbore')){
// findData=data.find(e=>e.type==='Wellbore')
// select =getValue(findData.parent?.parent.parent, 'label')
// let wellBoreName = findData.label
// let wellName = getValue(findData.parent, 'label')
// let blockName = getValue(findData?.parent.parent, 'label')
// let result =treeData.value.filter(e=>e.label===select)
// // dealRightTree(result,blockName,wellName,wellBoreName)
// }
// console.log('cccccccccccccccccccc',findData,select);
// choseTree.value=treeData.value.filter(e=>e.label===select)
// choseTreeStatus.value=false
// console.log('rrrrrrrrrrrrr','choseTree.value',choseTree.value);
// }
// if(data.type !== 'field'){
// let arr = node.checkedNodes.filter((ele) => {
// return ele.type === 'Wellbore'
// })
// if(node.checkedKeys!==0){
// ElMessage.warning('只可选择一个油田')
// treeRef.value.setChecked(data.label, false,true)
// console.log('bbbbbbbbbbbbbbbbbbbbbb');
// }else{
// treeRef.value.setChecked(data.label, true,true)
// }
// }
// if (data.type !== 'field') {
// let arr = node.checkedNodes.filter((ele) => {
// return ele.type === 'Wellbore'
// })
// if (!curObj.value.DisplayField) {
// curObj.value.DisplayField = field
// wellList.value = arr
// }
// if (curObj.value.DisplayField === field ) {
// wellList.value = arr
// } else {
// ElMessage.warning('只可选同一油田的')
// treeRef.value.setChecked(data.label, false,true)
// return false
// }
// wellList.value = [...new Set(wellList.value)]
// if (!arr.length) {
// curObj.value.DisplayField = ''
// }
// }
}
const handleSave = () => {
let list = wellList.value.map((ele) => {
return ele.label
})
curObj.value.DisplayWellboreList = list.join(',')
curObj.value.CurrentWellboreID = list[list.length - 1]
POST(appStore.baseURL + 'usr-wellbore-display', curObj.value)
.then((data) => {
ElMessage.success(data)
appStore.loadFulWellInfoData(true)
})
.catch((data) => {
ElMessage.error(data)
})
}
</script>
<style scoped lang="scss">
.el-tree {
background: none;
height: 100%;
--el-tree-node-hover-bg-color: rgb(37, 49, 68);
--el-tree-text-color: white;
}
.tree {
background: url('src/assets/images/treebg.png');
background-size: 100% 99%;
background-repeat: no-repeat;
.title {
color: white;
padding-left: 10px;
height: 5%;
}
}
.el-button {
background: rgb(15, 76, 138);
border: 1px solid rgb(0, 137, 241);
border-radius: 3px;
color: white;
box-shadow: 0px 0px 11px 0px #01c6f847;
}
li {
list-style: none;
}
</style>
transformData方法
/**
* 递归解析基础数据
* @param tempData
* @param parent
*/
export const transformData = (tempData: OBJ, parent?: FULLWELLINFODATA) => {
const result = []
for (const key in tempData) {
// 默认赋予油田属性
const wellInfo: FULLWELLINFODATA = {
uuid:uuidv4(),
label: key,
// label: uuidv4(),
name:key,
type: 'Field',
info: { Field: key },
}
if (parent) {
// 附加parent
wellInfo.parent = parent
}
if (Array.isArray(tempData[key])) {
// 基本信息
// const fieldName = wellInfo.parent?.parent?.label
// const blockName = wellInfo.parent?.label
const fieldName = wellInfo.parent?.parent?.name
const blockName = wellInfo.parent?.name
// 井筒
wellInfo.children = tempData[key].map((item: WELLBLOCK) => {
return {
// uuid: String,
info: {
Field: fieldName,
Block: blockName,
WellID: key,
...item,
},
type: 'Wellbore',
label: item.WellboreName,
// label:uuidv4(),
name: item.WellboreName,
uuid:uuidv4(),
parent: wellInfo,
} as FULLWELLINFODATA
})
// 井
wellInfo.label = key
// wellInfo.label=uuidv4()
wellInfo.name = key
wellInfo.uuid=uuidv4()
wellInfo.type = 'Well'
wellInfo.info = {
Field: fieldName,
Block: blockName,
WellID: key,
}
// 区块
const block = wellInfo.parent!
block.type = 'Block'
block.info = {
Field: fieldName,
Block: blockName,
}
} else {
wellInfo.children = transformData(tempData[key], wellInfo)
}
result.push(wellInfo)
}
return result
}
13.实现任意角度的扇形
<template>
<div class="ontology">
<div class="modelShow">
<div class="airscrewModel">
<div class="airscrewModelData">
<img src="../../assets/img/toLeft.png" alt="" class="directionIcon" v-show="this.roll.toString().includes('-')" />
<img src="../../assets/img/toRight.png" class="directionIcon" alt="" v-show="!this.roll.toString().includes('-')"/>
<span class="directionData">{{roll}}</span>
</div>
<div :style="{transform: `rotate(${roll}deg)`}" class="airscrewModelImg"></div>
</div>
<div class="submarine" v-show="!this.angel.toString().includes('-')">
<div class="chuan" :style="{ transform: `rotate(${'-'+angel}deg)` }"></div>
<svg width="300" height="300" xmlns="http://www.w3.org/2000/svg" class="upCircle">
<circle
r="150"
cx="0"
cy="300"
fill="transparent"
stroke="rgba(176, 176, 176, 0.2)"
stroke-width="300"
:stroke-dasharray="300 * 3.1415926"
:stroke-dashoffset="300 * 3.1415926 / 360 * (360 + angel)"
/>
</svg>
<hr class="linetwo" />
<div class="rotateData">
<img src="../../assets/img/toTop.png" alt="" class="directionIcon" />
<!-- <img src="../../assets/img/toBottom.png" class="directionIcon" alt="" /> -->
<span class="directionData">{{angel}}</span>
</div>
</div>
<div class="submarine" v-show="this.angel.toString().includes('-')">
<div class="downChuan" :style="{ transform: `rotate(${angel.toString().substring(1)}deg)` }"></div>
<svg width="480" height="250" xmlns="http://www.w3.org/2000/svg" class="downCircle">
<circle
r="150"
cx="10"
cy="10"
fill="transparent"
stroke="rgba(176, 176, 176, 0.2)"
stroke-width="300"
:stroke-dasharray="300 * 3.1415926"
:stroke-dashoffset="rotateData(angel)"
/>
</svg>
<hr class="linetwo" />
<div class="downRotateData">
<!-- <img src="../../assets/img/toTop.png" alt="" class="directionIcon" /> -->
<img src="../../assets/img/toBottom.png" class="directionIcon" alt="" />
<span class="directionData">{{angel}}</span>
</div>
</div>
</div>
<div class="modelIndex">
<div v-for="(item,index) in modelIndex" class="modelIndexItem">
<img :src="item.img" class="modelItemImg" alt=""/>
<div class="modelContent">
<p class="modelIndexFont">{{item.key}}</p>
<p class="modelIndexValue">
<span class="controlModelValue">{{item.value}}</span>
<span class="controlModelUnit">{{item.unit}}</span>
</p>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'Ontology',
props:{
controlObj: Object
},
data () {
return {
angel: -2.06,
// i: Math.floor(Math.random()*10),
roll: 30,
modelIndex:[
{
key:'编号',
img: require('../../assets/img/number.png'),
value: '3',
unit: ''
},
{
key:'主体',
img: require('../../assets/img/mainBody.png'),
value: '静默运行',
unit: ''
},
{
key:'航深',
img: require('../../assets/img/depthOfNavigation.png'),
value: '50',
unit: 'm'
},
{
key:'经度',
img: require('../../assets/img/longItude.png'),
value: '120',
unit: '°E'
},
{
key:'纬度',
img: require('../../assets/img/latitude.png'),
value: '35',
unit:'°N'
},
{
key:'航程',
img: require('../../assets/img/voyage.png'),
value: '200',
unit:'NA'
},
{
key:'对底深度',
img: require('../../assets/img/bottomDepth.png'),
value: '46',
unit:'M'
},
{
key:'电击电流',
img: require('../../assets/img/motorCurrent.png'),
value: '100',
unit:'A'
},
{
key:'电击电压',
img: require('../../assets/img/MotorVoltage.png'),
value: '400',
unit:'V'
},
{
key:'仪表电池电量',
img: require('../../assets/img/batteryLevel.png'),
value: '50',
unit:'%'
},
{
key:'动力电池电量',
img: require('../../assets/img/powerBatteryLevel.png'),
value: '70',
unit: '%'
},
{
key:'左上舵',
img: require('../../assets/img/upperShip.png'),
value: '10',
unit:'m'
},
{
key:'右下舵',
img: require('../../assets/img/leftShip.png'),
value: '20',
unit:'°'
},
{
key:'右上舵',
img: require('../../assets/img/rightShip.png'),
value: '15',
unit:'°'
},
{
key:'左下舵',
img: require('../../assets/img/upperShip.png'),
value: '3',
unit:'m'
},
]
}
},
watch:{
controlObj(newVal,oldVal){
if(newVal){
// console.log('66666666666666',newVal,newVal.pitchAngle);
this.modelIndex.forEach(e => {
if(e.key==="编号"){
e.value = newVal.id
}else if(e.key=== '主体'){
}else if(e.key=== '航深'){
e.value = newVal.shipDepth
}else if(e.key=== '经度'){
e.value = newVal.longitude
}else if(e.key=== '纬度'){
e.value = newVal.latitude
}else if(e.key=== '航程'){
e.value = newVal.range
}else if(e.key=== '对底深度'){
e.value = newVal.bottomDepth
}else if(e.key=== '电击电流'){
e.value = newVal.electricity
}else if(e.key=== '电击电压'){
e.value = newVal.voltage
}else if(e.key=== '仪表电池电量'){
e.value = newVal.dashbordBattery
}else if(e.key=== '动力电池电量'){
e.value = newVal.engineBattery
}else if(e.key=== '左上舵'){
e.value = newVal.rudderLeftTop
}else if(e.key=== '左下舵'){
e.value = newVal.rudderLeftBottom
}else if(e.key=== '右上舵'){
e.value = newVal.rudderRightTop
}else if(e.key=== '右下舵'){
e.value = newVal.rudderRightBottom
}
});
this.angel=Number(newVal.pitchAngle)
this.roll=Number(newVal.roll)
}
}
},
methods: {
rotateData(angle){
let str=angle.toString()
if(str.includes('-')){
const data = str.substring(1)
return 300 * 3.1415926 / 360 * (360 - data)
}else{
// console.log('+++++++++++++++++++++',300 * 3.1415926 / 360 * (360 - angle));
return 300 * 3.1415926 / 360 * (360 - angle)
}
}
},
created() {
},
mounted() {
},
}
</script>
<style lang="sass" scoped>
.ontology
width: 100%
height: 594px
.modelShow
width: 100%
height: 286px
position: relative
.modelIndex
display: flex
flex-wrap: wrap
.modelIndexItem
display: flex
width: 33%
height: 40px
margin-bottom: 16px
padding-left: 23px
box-sizing: border-box
.modelItemImg
width: 36px
height: 36px
background-size: 100%
.modelIndexFont
font-family: MiSans;
font-size: 14px;
color: rgba(168, 214, 255, 1);
margin: 0
.modelIndexValue
font-family: Furore;
font-size: 18px;
color: rgba(255, 255, 255, 1);
margin: 0
.controlModelValue
font-family: Furore;
font-size: 18px;
color: rgba(255, 255, 255, 1);
.controlModelUnit
font-family: PingFang SC;
font-size: 14px;
color: rgba(222, 240, 255, 1);
.modelContent
display: inline-block
margin-left: 10px
.circleBlock
width: 200px;
height: 200px;
position: relative;
overflow: hidden;
border-radius: 50%;
.circle
width: 50%;
height: 50%;
background: #6C609E
position: absolute;
top: 0
right: 0
transform-origin: 0% 100%
overflow: hidden
transform: skewY(-45deg)
.rotateModel
position: absolute
bottom: -300px;
background-color: red;
width: 600px;
height: 600px;
border-radius: 50%;
clip-path: polygon(50% 50%, 20% 0%, 80% 0%);
.airscrewModel
width: 176px
height: 110px
top: 4px
left: 23px
position: absolute
.airscrewModelImg
width: 100%
height: 100px
transition: all 3s
background: url('../../assets/img/airscrew.png') no-repeat center center
.airscrewModelData
display: flex
.directionIcon
width: 24px
height: 24px
.directionData
font-family: MiSans;
font-size: 18px;
font-weight: 500;
color: rgba(255, 255, 255, 1);
.submarine
width: 100%;
height: 100%
box-sizing: border-box
.rotateData
position: absolute
bottom: 90px
right: 0
display: flex
.downRotateData
position: absolute
bottom: 90px
left: 30px
display: flex
</style>
<style scoped>
.upCircle{
/* transform: rotate(-30deg);
transform-origin: center center; */
position: absolute;
left: 100px;
bottom: 65px;
/* margin-top: -150px; */
z-index: 2;
}
.downCircle{
transform: rotate(-180deg);
transform-origin: center center;
position: absolute;
left: -66px;
bottom: 61px;
}
.chuan, .downChuan {
/* // display: block; */
height: 100px;
width: 300px;
position: absolute;
bottom: 68px;
left: 100px;
/* // height: 5px; */
z-index: 3;
background-color: transparent !important;
transition: all 3s;
transform-origin: left bottom;
border-bottom: 1px dashed #fff;
background: url('../../assets/img/submarine.png') no-repeat center center;
}
.chuanbacground {
position: absolute;
left: 121px;
bottom: -40px;
/* margin-top: -150px; */
z-index: 2;
/* background-color: rgba(176, 176, 176, 0.2); */
/* width: 250px;
height: 250px; */
/* border-radius: 50%; */
/* // clip-path: polygon(50% 50%, 20% 0%, 80% 0%); */
/* clip-path: polygon(100% 50%, 0 50%, 0 0, 100% 0); */
/* // clip-path: polygon(100% 50%, 0 50%, 0 0, 100% 0); */
/* // clip-path: polygon(50% 0, 100% 0, 100% 3600%, 50% 50%); */
}
.downChuan{
bottom: 70px;
transform-origin: right bottom;
}
.linetwo {
/* // width: 50%; */
width: 300px;
/* margin-top: -5px; */
/* // margin-left: -472px; */
border: 1px dashed;
position: absolute;
border: 1px dashed #fff;
bottom: 60px;
left: 100px;
}
</style>
效果截图
14.手写一个进度条
<template>
<div class="slider" ref="slider" @click.stop="handelClickSlider">
<div class="process" :style="{ width,background:bgColor }"></div>
<div class="thunk" ref="trunk" :style="{ left }">
<div class="block" ref="dot"></div>
</div>
</div>
</template>
<script>
/*
* min 进度条最小值
* max 进度条最大值
* v-model 对当前值进行双向绑定实时显示拖拽进度
* */
import { mapActions,mapState } from 'vuex';
export default {
name: 'Slisd',
props: {
// 最小值
title: String,
min: {
type: Number,
default: 0,
},
// 最大值
max: {
type: Number,
default: 100,
},
// 当前值
value: {
type: Number,
default: 0,
},
// 进度条颜色
bgColor: {
type: String,
default: "#4ab157",
},
// 是否可拖拽
isDrag: {
type: Boolean,
default: true,
},
},
data() {
return {
slider: null, //滚动条DOM元素
thunk: null, //拖拽DOM元素
per: this.value, //当前值
// sendSEMsg: false,
currentWidth: null,
currentStatus: null,
number: 0
};
},
mounted() {
this.slider = this.$refs.slider;
this.thunk = this.$refs.trunk;
var _this = this;
if (!this.isDrag) return;
this.thunk.onmousedown = function (e) {
var width = parseInt(_this.width);
var disX = e.clientX;
document.onmousemove = function (e) {
// value, left, width
// 当value变化的时候,会通过计算属性修改left,width
// 拖拽的时候获取的新width
var newWidth = e.clientX - disX + width;
// 计算百分比
var scale = newWidth / _this.slider.offsetWidth;
_this.per = Math.ceil((_this.max - _this.min) * scale + _this.min); //取整
// 限制值大小
_this.per = Math.max(_this.per, _this.min);
_this.per = Math.min(_this.per, _this.max);
_this.$emit("input", _this.per);
_this.$store.state.slideStatus = true
};
document.onmouseup = function () {
//当拖拽停止发送事件
_this.$emit("stop", _this.per);
//清除拖拽事件
document.onmousemove = document.onmouseup = null;
// this.sendSEMsg = true
this.number = 1
};
};
},
methods: {
handelClickSlider(event) {
this.$store.state.slideStatus = true
//禁止点击
if (!this.isDrag) return;
const dot = this.$refs.dot;
if (event.target == dot) return;
//获取元素的宽度l
let width = this.slider.offsetWidth;
//获取元素的左边距
let ev = event || window.event;
//获取当前点击位置的百分比
let scale = ((ev.offsetX / width) * 100).toFixed(2);
this.per = scale;
},
...mapActions('fui-commands', { createFuiCommands: 'create' }),
async sendMesToSE(value,status){
let obj={}
if(this.title==="12 小时光照"){
obj = {
userId: this.$store.state.auth.user._id,
sources: 'fui',
command: 'illuminationStretch',
data: status.toFixed(2),
};
}else if(this.title==="海浪等级"){
obj = {
userId: this.$store.state.auth.user._id,
sources: 'fui',
command: 'seaWaveStretch',
data: (Number(status)+1).toString().substring(0,1),
};
}else {
obj = {
userId: this.$store.state.auth.user._id,
sources: 'fui',
command: 'underwaterBrightness',
data: (Number(status)+1).toString().substring(0,1),
};
}
const res = await this.createFuiCommands(obj);
// console.log('-----------------------',this.title,res,status);
}
},
computed: {
...mapState(['slideStatus']),
// 设置一个百分比,提供计算slider进度宽度和trunk的left值
// 对应公式为 当前值-最小值/最大值-最小值 = slider进度width / slider总width
// trunk left = slider进度width + trunk宽度/2
scale() {
return (this.per - this.min) / (this.max - this.min);
},
width() {
return this.slider ? this.slider.offsetWidth * this.scale + "px" : "0px";
},
left() {
return this.slider ? this.slider.offsetWidth * this.scale - this.thunk.offsetWidth / 2 +"px" : "0px";
},
},
watch: {
value: {
handler: function () {
this.per = this.value;
},
},
width: function(newVal,oldVal){
// console.log('ccccccccccccccccccc',newVal,oldVal,this.$store.state,this.$store.state.slideStatus);
let data = newVal.slice(0,-2)
let scrollStatus = Number(data).toFixed()/416
let status
if(this.title==="12 小时光照"){
status = 12*scrollStatus
this.$emit('scrollStatus',status)
}else if(this.title==="海浪等级") {
status = 3*scrollStatus
this.$emit('scrollStatus',status)
}else {
status = 3*scrollStatus
this.$emit('scrollStatus',status)
}
this.currentWidth = newVal
this.currentStatus = status
// console.log('........................',status);
if(this.$store.state&&this.$store.state.slideStatus){
console.log('send SE message');
this.sendMesToSE(newVal,status)
}
},
},
};
</script>
<style lang="sass" scoped>
/* @import url(); 引入css类 */
</style>
<style scoped>
.box {
margin: 100px auto 0;
width: 80%;
}
.clear:after {
content: "";
display: block;
clear: both;
}
.slider {
position: relative;
margin: 9px 0;
top: 50%;
width: 100%;
height: 12px;
background: rgba(59, 59, 59, 0.38);
border: 1px solid rgba(189, 189, 189, 1);
/* background: #747475;
border-radius: 5px; */
cursor: pointer;
z-index: 99999;
}
.slider .process {
position: absolute;
left: 0;
top: 0;
width: 112px;
height: 10px;
border-radius: 5px;
/* background: #4ab157; */
background: linear-gradient(270deg, rgba(255, 255, 255, 0.8) 0%, rgba(255, 255, 255, 0) 100%);
z-index: 111;
}
.slider .thunk {
position: absolute;
left: 100px;
top: -4px;
width: 10px;
height: 6px;
z-index: 122;
}
.slider .block {
width: 11px;
height: 18px;
/* border-radius: 50%; */
/* background: rgba(255, 255, 255, 1); */
background: url('../../assets/img/scheduleHead.png') no-repeat;
transition: 0.2s all;
}
.slider .block:hover {
transform: scale(1.1);
opacity: 0.6;
}
</style>
组件使用
<template>
<div>
<div class="progressTitle">
<span class="processFont">{{title}}</span>
<div class="processTime">{{title==='12 小时光照'?status:status.toString().substring(0,1)+'级'}}</div>
</div>
<!-- <div class="processBar">
<div class="scrollBar" :style="{width:scroll+'px'}"></div>
<img src="../../assets/img/scheduleHead.png" alt="" class="scrollDrop"/>
</div> -->
<Slisd :title="title" :min="0" :max="100" :value="50" :isDrag="true" bgColor="linear-gradient(270deg, rgba(255, 255, 255, 0.8) 0%, rgba(255, 255, 255, 0) 100%)" @scrollStatus="scrollStatus"></Slisd>
<div class="timeFiled">
<div class="tipField">{{start}}</div>
<div class="tipField">{{end}}</div>
</div>
</div>
</template>
<script>
import Slisd from '../slisd/index.vue'
export default {
name: 'SceneConfigItem',
components:{
Slisd
},
props:{
scroll: String,
title:String,
// status:String,
start: String,
end: String
},
data () {
return {
status: 0
}
},
methods: {
scrollStatus(value){
// console.log('hhhhhhhhhhhhhhh',value,Number(value.toFixed(0)));
if(this.title==='12 小时光照'){
let data = value.toFixed(2)
this.status = data.toString().replace('.',':')
}else if(this.title==='海浪等级') {
let data = Number(value)+1
this.status = data.toFixed(2)+ '级'
}else {
let data = Number(value)+1
this.status = data.toFixed(2)+ '级'
}
}
},
created () {
},
mounted () {
},
}
</script>
<style lang="sass" scoped>
.progressTitle
width: 100%
height: 27px
.processFont
font-family: MiSans;
font-size: 20px;
color: rgba(255, 255, 255, 1);
.processTime
float: right
display: inline-block
width: 55px
height: 22px
text-align: center
line-height: 22px
border-radius: 2px
border: 1px solid rgba(11, 219, 94, 1)
background: linear-gradient(0deg, #0BDB5E, #0BDB5E),linear-gradient(0deg, #0BDB5E, #0BDB5E);
font-family: MiSans;
font-size: 12px;
font-weight: 500;
color: rgba(255, 255, 255, 1);
.processBar
margin: 6px 0;
display: flex;
align-items: center
width: 100%
height: 12px
background: rgba(59, 59, 59, 0.38);
border: 1px solid rgba(189, 189, 189, 1)
.scrollBar
display: inline-block
width: 100px
height: 12px
background: linear-gradient(270deg, rgba(255, 255, 255, 0.8) 0%, rgba(255, 255, 255, 0) 100%);
.scrollDrop
display: inline-block
width: 11px
height: 18px
.timeFiled
margin-bottom: 10px
width: 100%;
height: 24px
display: flex;
justify-content: space-between
align-items: center
.tipField
padding: 4px 10px 4px 10px
box-sizing: border-box
color: rgba(15, 15, 15, 0.34);
font-family: MiSans;
font-size: 12px;
color: rgba(255, 255, 255, 1);
background: rgba(15, 15, 15, 0.34);
</style>
效果图如下