123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526 |
- <template>
- <view class="container">
- <div style="display: flex;flex-direction: column;">
- <div style="display: flex;">
- <button type="primary" style="width:100%;margin-bottom: 10px;" @click="switchRoles">
- 交换
- </button>
- <button type="primary" style="width:100%;margin-bottom: 10px;" @click="calculateBestMatch">
- 计算最佳配对
- </button>
- </div>
- <uni-table border stripe emptyText="暂无更多数据">
- <uni-tr>
- <uni-th width="55px">角色</uni-th>
- <uni-th align="left" width="55px">数量</uni-th>
- <uni-th align="left" width="55px">性别</uni-th>
- <uni-th align="left" width="120px">势力</uni-th>
- <uni-th align="left" width="37px">操作</uni-th>
- </uni-tr>
- <uni-tr v-for="(item, index) in inputData" :key="index">
- <uni-td rowspan="1">{{ item.role }}</uni-td>
- <uni-td>{{ item.num }}</uni-td>
- <uni-td>{{ item.gender }}</uni-td>
- <uni-td class="text-power">
- <view>{{ item.power }}</view>
- </uni-td>
- <uni-td>
- <uni-icons class="edit-button" type="compose" size="16"
- @click="editRow(item.gender, item.role, index)" />
- </uni-td>
- </uni-tr>
- </uni-table>
- </div>
- <!-- 输出表格 -->
- <view class="section" v-if="outputData.length > 0">
- <div style="display: flex;justify-content: space-between;margin-top:10px">
- <text class="section-title">输出, 计算时间 {{ time }} ms </text>
- <span>差额: {{ powerDifference }}</span>
- </div>
- <uni-table border stripe emptyText="暂无更多数据">
- <uni-tr>
- <uni-th width="60px" rowspan="2">顺序</uni-th>
- <uni-th align="left" colspan="2" width="120px">目标</uni-th>
- <uni-th align="left" colspan="2" width="120px">备选</uni-th>
- </uni-tr>
- <uni-tr>
- <uni-th align="left" width="60px">目标</uni-th>
- <uni-th align="left" width="60px">备选</uni-th>
- <uni-th align="left" width="60px">目标</uni-th>
- <uni-th align="left" width="60px">备选</uni-th>
- </uni-tr>
- <uni-tr v-for="(item, index) in outputData" :key="index">
- <uni-td>{{ index + 1 }}</uni-td>
- <uni-td>{{ item.targetMale }}</uni-td>
- <uni-td>{{ item.targetFemale }}</uni-td>
- <uni-td>{{ item.backupMale }}</uni-td>
- <uni-td>{{ item.backupFemale }}</uni-td>
- </uni-tr>
- <uni-tr>
- <uni-td>合计</uni-td>
- <uni-td colspan="2">{{ totalTarget }}</uni-td>
- <uni-td colspan="2">{{ totalBackup }}</uni-td>
- </uni-tr>
- </uni-table>
- </view>
- <uni-popup ref="batchRef" :is-mask-click="false">
- <div
- style="display: flex;flex-direction: column;justify-content: center;padding: 10px; width: 95%;background: #fff;border-radius: 10px;padding: 10px;">
- <div style="display: flex;justify-content: space-between;align-items: center;margin-bottom: 10px;">
- <span></span>
- <h4 style="margin-bottom: 10px"> 编辑{{ role }}</h4>
- <uni-icons type="closeempty" size="18" @click="batchRef.close()"></uni-icons>
- </div>
- <div style="display: flex;justify-content: space-between; align-items: center;" v-if="gender === '公'">
- <span class="dialog-title">猫咪势力(公)</span>
- <uni-icons type="close" size="18" @click="malePowers = ''"></uni-icons>
- </div>
- <textarea v-model="powers" placeholder="用逗号或顿号隔开,如:200,10,100"
- style="height: 70px;border: 1px solid #dfe2e5" v-if="gender === '公'" />
- <div style="display: flex;justify-content: space-between; align-items: center;" v-if="gender === '母'">
- <span class="dialog-title">猫咪势力(母)</span>
- <uni-icons type="close" size="18" @click="feMalePowers = ''"></uni-icons>
- </div>
- <textarea v-model="powers" placeholder="用逗号或顿号隔开,如:200,10,100"
- style="height: 70px;border: 1px solid #dfe2e5" v-if="gender === '母'" />
- <button size='mini' type="primary" @click="handleConfirm">保存</button>
- </div>
- </uni-popup>
- <uni-popup ref="messageRef" type="message">
- <uni-popup-message type="error" :message="messageText" :duration="5000"></uni-popup-message>
- </uni-popup>
- </view>
- </template>
- <script setup>
- import { ref } from 'vue';
- const messageText = ref('')
- const messageRef = ref(null)
- const total = ref(0)
- const index = ref(-1)
- const time = ref("")
- const role = ref("")
- const type = ref('')
- const gender = ref("")
- const powers = ref("")
- const malePowers = ref('')
- const feMalePowers = ref('')
- const totalTarget = ref(0)
- const totalBackup = ref(0)
- const mockData = [
- {
- role: '目标',
- num: 0,
- gender: '公',
- power: ''
- },
- {
- role: '目标',
- num: 0,
- gender: '母',
- power: ''
- },
- {
- role: '备选',
- num: 0,
- gender: '公',
- power: ''
- },
- {
- role: '备选',
- num: 0,
- gender: '母',
- power: ''
- }
- ]
- const inputData = ref(mockData);
- const outputData = ref([]);
- const batchRef = ref(null)
- function editRow(_gender, _role, _index) {
- type.value = 'edit'
- gender.value = _gender
- role.value = _role
- index.value = _index
- const { power } = inputData.value[_index]
- powers.value = power
- batchRef.value.open()
- }
- function switchRoles(){
- outputData.value = []
- const [t1, t2, b1, b2] = inputData.value
- inputData.value[0] = { ...b1 }
- inputData.value[1] = { ...b2 }
- inputData.value[2] = { ...t1 }
- inputData.value[3] = { ...t2 }
- }
- const powerDifference = ref(0)
- function isPositiveInteger(str) {
- return /^[1-9]\d*$/.test(str);
- }
- function generatePowers(_powers){
- _powers = _powers.replace(/,|、|,/g, ',')
- const powerSet = _powers.split(',').map(item => item.trim()).filter(item => item !== '')
- const isOk = powerSet.every(num => isPositiveInteger(num))
- if (!isOk && powerSet.length !== 0) return false
- inputData.value[index.value].num = powerSet.length;
- inputData.value[index.value].power = powerSet.join(',')
- const [t1, t2] = inputData.value
- const t1ps = t1.power.split(',')
- const t2ps = t2.power.split(',')
- }
- function handleConfirm(){
- messageRef.value.close()
- const isOk = generatePowers(powers.value, '公')
- if (typeof isOk === 'boolean' && !isOk){
- messageRef.value.close()
- messageRef.value.open()
- messageText.value = '势力必须为数字'
- return
- }
- batchRef.value.close()
- }
- // 生成数组的所有组合(辅助函数,用于生成指定个数元素的组合情况)
- function getCombinations(arr, num) {
- const result = [];
- if (num === 0) { return [[]]; }
- for (let i = 0; i < arr.length; i++) {
- const element = arr[i];
- const remainingCombos = getCombinations(arr.slice(i + 1), num - 1);
- for (const combo of remainingCombos) {
- result.push([element].concat(combo));
- }
- }
- return result;
- }
- function calculateBestMatch() {
- let startTime = new Date().getTime()
- let endTime = null
-
- outputData.value = []
- const targetMale = inputData.value[0].power.split(',').filter(item => item !== '').map(Number)
- const targetFemale = inputData.value[1].power.split(',').filter(item => item !== '').map(Number)
- const backupMale = inputData.value[2].power.split(',').filter(item => item !== '').map(Number)
- const backupFemale = inputData.value[3].power.split(',').filter(item => item !== '').map(Number)
- if (targetFemale.length === 0 && targetFemale.length === 0) {
- messageRef.value.open()
- messageText.value = '目标公数据与母数据不能同时为空'
- return
- }
- if(targetFemale.length + targetFemale.length > 20){
- messageRef.value.open()
- messageText.value = '目标公数据与母数据的和不能超过20条'
- return
- }
-
- if(backupMale.length > 20){
- messageRef.value.open()
- messageText.value = '备选的公数据不能超过20条'
- return
- }
-
- if(backupFemale.length > 20){
- messageRef.value.open()
- messageText.value = '备选的母数据不能超过20条'
- return
- }
-
- if (targetFemale.length > backupMale.length && backupMale.length !== 0){
- messageRef.value.open()
- messageText.value = '备选的公数据长度必须大于或等于目标的母数据'
- return
- }
- if (targetMale.length > backupFemale.length && backupFemale.length !== 0) {
- messageRef.value.open()
- messageText.value = '备选的母数据长度必须或等于大于目标的公数据'
- return
- }
- const targetMaleLen = targetMale.length;
- const targetFemaleLen = targetFemale.length;
- let targetWeight = 0
- if (backupMale.length === 0){
- targetWeight = targetMale.reduce((acc, val) => acc + val, 0);
- } else if(backupFemale.length === 0){
- targetWeight = targetFemale.reduce((acc, val) => acc + val, 0)
- } else if(backupMale.length !== 0 && backupFemale.length !== 0){
- targetWeight = targetMale.reduce((acc, val) => acc + val, 0) + targetFemale.reduce((acc, val) => acc + val, 0);
- }
- let minDifference = Infinity;
- let bestfemaleCombination = null;
- let bestMaleCombination = null;
- const femaleCombinations = getCombinations(backupFemale, targetMaleLen);
- const maleCombinations = getCombinations(backupMale, targetFemaleLen);
- if (femaleCombinations.length !== 0 && maleCombinations.length !== 0){
- for (const femaleComb of femaleCombinations) {
- for (const maleComb of maleCombinations) {
- const totalWeight = femaleComb.reduce((acc, val) => acc + val, 0) + maleComb.reduce((acc, val) => acc + val, 0);
- const difference = Math.abs(totalWeight - targetWeight);
- if (difference < minDifference) {
- minDifference = difference;
- bestfemaleCombination = femaleComb;
- bestMaleCombination = maleComb;
- if (minDifference == 0) {
- break;
- }
- }
- }
- }
- }
- // 备选数据的母为空
- if (femaleCombinations.length === 0 && maleCombinations.length !== 0) {
- for (const maleComb of maleCombinations) {
- const totalWeight = maleComb.reduce((acc, val) => acc + val , 0);
- const difference = Math.abs(totalWeight - targetWeight);
- if (difference < minDifference) {
- minDifference = difference;
- bestMaleCombination = maleComb;
- if (minDifference == 0) {
- break;
- }
- }
- }
- }
- // 备选数据的公为空
- if (femaleCombinations.length !== 0 && maleCombinations.length === 0) {
- for (const femaleComb of femaleCombinations) {
- const totalWeight = femaleComb.reduce((acc, val) => acc + val, 0)
- const difference = Math.abs(totalWeight - targetWeight);
- if (difference < minDifference) {
- minDifference = difference;
- bestfemaleCombination = femaleComb;
- // bestMaleCombination = maleComb;
- if (minDifference == 0) {
- break;
- }
- }
- }
- }
- bestMaleCombination && bestMaleCombination.sort((a, b) => b - a)
- bestfemaleCombination && bestfemaleCombination.sort((a, b) => b - a)
- targetMale.sort((a, b) => b - a)
- targetFemale.sort((a, b) => b - a)
- if (!bestMaleCombination && !bestfemaleCombination){ return }
- bestfemaleCombination && targetMale.forEach((_, index) => {
- outputData.value.push({
- targetMale: targetMale[index],
- targetFemale: null,
- backupMale: null,
- backupFemale: bestfemaleCombination[index]
- })
- })
-
-
- const fl = targetFemale.length
-
- for(let index = 0; index < fl; index ++){
- if(outputData.value[index]){
- outputData.value[index].targetFemale = targetFemale[index]
- outputData.value[index].backupMale = bestMaleCombination[index]
- } else {
- outputData.value.push({
- targetMale: null,
- targetFemale: targetFemale[index],
- backupMale: bestMaleCombination[index],
- backupFemale: null
- })
-
- }
- }
- totalTarget.value = outputData.value.reduce((prev, current) => prev + current.targetMale, 0) + outputData.value.reduce((prev, current) => prev + current.targetFemale, 0)
- totalBackup.value = outputData.value.reduce((prev, current) => prev + current.backupMale, 0) + outputData.value.reduce((prev, current) => prev + current.backupFemale, 0)
- powerDifference.value = totalTarget.value - totalBackup.value
- endTime = new Date().getTime()
- time.value = endTime - startTime
- }
- </script>
- <style>
- .text-power {
- width: 120px;
- max-width: 120px;
- overflow: hidden;
- text-overflow: ellipsis;
- text-wrap: nowrap;
- /* white-space: pre-wrap; */
- }
- .edit-button {
- color: #409eff !important;
- cursor: pointer;
- }
- textarea {
- font-size: 13px;
- padding: 8px;
- }
- </style>
- <style lang="scss" scoped>
- button {
- height: 30px;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 14px;
- }
- .dialog-title {
- line-height: 30px;
- padding-left: 10px;
- font-size: 14px;
- }
- .table-wrapper {
- font-size: 14px !important;
- }
- .custom-picker {
- border: 1px solid #e3e3e3;
- padding: 3px 5px;
- text-align: center;
- width: 100%;
-
-
- div {
- display: flex;
- justify-content: space-between;
-
- text {
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- }
- }
- }
- .container {
- padding: 10px 20px;
- }
- .section {
- margin-bottom: 20px;
- }
- .section-title {
- font-size: 18px;
- font-weight: bold;
- margin-bottom: 10px;
- }
- .input {
- width: 100%;
- text-align: center;
- border: 1px solid #ddd;
- padding: 5px;
- }
- button {
- margin: 5px;
- }
- .add-btn,
- .calculate-btn {
- background-color: #007bff;
- color: white;
- padding: 10px;
- font-size: 16px;
- text-align: center;
- border-radius: 5px;
- }
- .delete-btn {
- background-color: #ff4d4f;
- color: white;
- padding: 5px;
- font-size: 14px;
- border-radius: 5px;
- }
- .summary {
- margin-top: 20px;
- font-size: 14px;
- text-align: right;
- font-weight: bold;
- }
- </style>
|