Browse Source

fix bug #164

tags/v1.21.10.1^2
zhoupzh 3 years ago
parent
commit
f8039fae41
3 changed files with 721 additions and 221 deletions
  1. +52
    -7
      templates/repo/home.tmpl
  2. +414
    -0
      web_src/js/components/EditTopics.vue
  3. +255
    -214
      web_src/js/index.js

+ 52
- 7
templates/repo/home.tmpl View File

@@ -55,6 +55,40 @@
#contributorInfo > a.circular:nth-child(9n+8){
background-color: #bfd0aa;
}
.vue_menu {
cursor: auto;
position: absolute;
outline: none;
top: 100%;
margin: 0em;
padding: 0em 0em;
background: #fff;
font-size: 1em;
text-shadow: none;
text-align: left;
/* -webkit-box-shadow: 0px 2px 3px 0px rgb(34 36 38 / 15%); */
box-shadow: 0px 2px 3px 0px rgba(34, 36, 38, 0.15);
border: 1px solid rgba(34,36,38,0.15);
border-radius: 0.28571429rem;
-webkit-transition: opacity 0.1s ease;
transition: opacity 0.1s ease;
z-index: 11;
will-change: transform, opacity;
width: 100% !important;
-webkit-animation-iteration-count: 1;
animation-iteration-count: 1;
-webkit-animation-duration: 300ms;
animation-duration: 300ms;
-webkit-animation-timing-function: ease;
animation-timing-function: ease;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
}



</style>
<div class="repository file list">
{{template "repo/header" .}}
@@ -62,7 +96,7 @@
{{template "base/alert" .}}
{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}
<div class="ui repo-topic-edit grid form segment error" id="topic_edit" style="display:none">
<!-- <div class="ui repo-topic-edit grid form segment error" id="topic_edit" style="display:none">
<div class="fourteen wide column">
<div class="field">
<div class="ui fluid multiple search selection dropdown">
@@ -78,7 +112,8 @@
<a class="ui button primary" href="javascript:;" id="save_topic"
data-link="{{.RepoLink}}/topics">{{.i18n.Tr "repo.topic.done"}}</a>
</div>
</div>
</div> -->
{{end}}
<div class="hide" id="validate_prompt">
<span id="count_prompt">{{.i18n.Tr "repo.topic.count_prompt"}}</span>
@@ -232,11 +267,21 @@
{{end}}

<p class="ui" id="repo-topics">
<i class="grey bookmark icon"></i>
{{range .Topics}}<a class="ui repo-topic small label topic" href="{{AppSubUrl}}/explore/repos?q={{.Name}}&topic=1">{{.Name}}</a>{{end}}
{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}<a id="manage_topic">{{.i18n.Tr "repo.topic.manage_topics"}}</a>{{end}}
</p>
<div class="ui" id="repo-topics" style="display: flex;position: relative;margin-bottom: 1.0rem;">
<i class="grey bookmark icon"></i>
<div id="repo-topics1" style="flex: 1;">
{{range .Topics}}<a class="ui repo-topic small label topic" href="{{AppSubUrl}}/explore/repos?q={{.Name}}&topic=1">{{.Name}}</a>{{end}}
</div>
<div>
{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}<i id="manage_topic" class="plus icon"></i>{{end}}
</div>
<div id="topic_edit" class="vue_menu" style="display:none">
<div id="topic_edit1">
</div>
</div>
</div>
<p class="ui">


+ 414
- 0
web_src/js/components/EditTopics.vue View File

@@ -0,0 +1,414 @@
<template>
<div>
<div class="input-search">
<el-input v-model="input" clearable :autofocus="true" @input="changeValue" id="topics_input">

</el-input>
<div class="scrolling-menu">
<div v-if="showSearchTopic" class="item-text" v-for="(arr,i) in array" @click="addTopics(i,arr)">
<div class="icon-wrapper">
<i style="line-height: 1.5;" v-if="showInitTopic[i]" class="el-icon-check" ></i>
</div>
<div class="text">{{arr.topic_name}} </div>
</div>
<div v-if="showInputValue" class="addition item-text" @click="postTopic">
点击或回车添加<b class="user-add-label-text">{{input}}</b>标签
</div>
<div v-if="showAddTopic" class="item-text" @click="addPostTopic">
<div class="icon-wrapper">
<i style="line-height: 1.5;" v-if="showAddFlage" class="el-icon-check" ></i>
</div>
<div class="text">{{input}}</div>
</div>

</div>

</div>
</div>
</template>

<script>

const {AppSubUrl, StaticUrlPrefix, csrf} = window.config;

import $ from 'jquery'


export default {
data() {
return {
input:'',
params:{},
showInputValue:false,
showFlag:-1,
array:[],
showAddTopic:false,
showAddFlage:false,
showSearchTopic:true,
postUrl:'',
arrayTopics:[],
showInitTopic:[],

};
},
methods: {

addTopics(item,array){
if(!this.showInitTopic[item]){

this.arrayTopics.push(array.topic_name)
let topics = this.arrayTopics
let strTopics = topics.join(',')
let data = this.qs.stringify({
_csrf:csrf,
topics:strTopics
})
this.Post(data,topics)
this.$set(this.showInitTopic,item,true)

}else{
this.arrayTopics=this.arrayTopics.filter(ele=>{
return ele !== array.topic_name

})

let topics = this.arrayTopics
let strTopics = topics.join(',')
let data = this.qs.stringify({
_csrf:csrf,
topics:strTopics
})
this.Post(data,topics)
this.$set(this.showInitTopic,item,false)

}
},
changeValue(){
console.log("changevalue")
if (this.input === ''){
this.array = this.arrayTopics
let data = []
this.showInitTopic = []
this.array.forEach((element,index) => {
let item = {}
item.topic_name = element
data.push(item)
this.showInitTopic.push(true)
});
this.array = data
this.showInputValue = false
this.showSearchTopic = true
}
else if(this.arrayTopics.indexOf(this.input)>-1){
this.showInputValue = false
this.showSearchTopic = false
}else{
this.showInitTopic = []
let timestamp=new Date().getTime()
this.params.q = this.input
this.params._ = timestamp
this.$axios.get('/api/v1/topics/search',{
params:this.params
}).then((res)=>{
this.array = res.data.topics
})
this.array.forEach((element,index) => {
if (this.arrayTopics.indexOf(element.topic_name)>-1){
this.showInitTopic.push(true)

}else{
this.showInitTopic.push(false)
}
});
this.showInputValue = true
this.showSearchTopic = true
}
this.showAddTopic = false

},
Post(data,topics){
this.$axios.post(this.postUrl,data).then(res=>{
const viewDiv = $('#repo-topics1');
viewDiv.children('.topic').remove();
if (topics.length) {
const topicArray = topics;
const last = viewDiv.children('a').last();
for (let i = 0; i < topicArray.length; i++) {
const link = $('<a class="ui repo-topic small label topic"></a>');
link.attr(
'href',
`${AppSubUrl}/explore/repos?q=${encodeURIComponent(
topicArray[i]
)}&topic=1`
);
link.text(topicArray[i]);
// link.insertBefore(last);
viewDiv.append(link)
}
}
viewDiv.show();
})
},
postTopic(){
let topic = this.input
this.arrayTopics.push(topic)
let topics = this.arrayTopics
let strTopics = topics.join(',')
let data = this.qs.stringify({
_csrf:csrf,
topics:strTopics
})
this.Post(data,topics)
this.showInputValue = false
this.showAddTopic = true
this.showAddFlage = true

},
addPostTopic(){
if(this.showAddFlage){
this.arrayTopics.pop()
let topics = this.arrayTopics
let strTopics = topics.join(',')
let data = this.qs.stringify({
_csrf:csrf,
topics:strTopics
})
this.Post(data,topics)

}
else if(!this.showAddFlage){
let topic = this.input
this.arrayTopics.push(topic)
let topics = this.arrayTopics
let strTopics = topics.join(',')
let data = this.qs.stringify({
_csrf:csrf,
topics:strTopics
})
this.Post(data,topics)
}
this.showAddFlage = !this.showAddFlage
},
initTopics(){
const mgrBtn = $('#manage_topic');
const editDiv = $('#topic_edit');
mgrBtn.on('click', (e) => {
// viewDiv.hide();
editDiv.css('display', ''); // show Semantic UI Grid
this.input = ''
console.log("this.",this)
console.log("-----------------asdasd",$("#topics_input"),$("#topics_input").val())
stopPropagation(e);
});
$(document).bind('click',function(){
editDiv.css('display','none');

})
editDiv.click(function(e){
stopPropagation(e);
})


function stopPropagation(e) {
var ev = e || window.event;
if (ev.stopPropagation) {
ev.stopPropagation();
}
else if (window.event) {
window.event.cancelBubble = true;//兼容IE
}
}
}
},
computed:{
},
watch: {

input(newValue){
console.log("---newvalue--",newValue)
if (newValue === ''){
this.array = this.arrayTopics
let data = []
this.showInitTopic = []
this.array.forEach((element,index) => {
let item = {}
item.topic_name = element
data.push(item)
this.showInitTopic.push(true)
});
this.array = data
this.showInputValue = false
this.showSearchTopic = true
}
}
},
mounted() {
const context = this
this.postUrl = `${window.location.pathname}/topics`;
$('#repo-topics1').children('a').each(function(){
context.arrayTopics.push($(this).text())
});
this.changeValue()
} ,
created(){
console.log("this.created");
this.initTopics();
this.input=''
}
};
</script>

<style scoped>
.input-search {
width: 100%;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
min-width: 10rem;
white-space: nowrap;
font-size: 1rem;
position: relative;
display: inline-block;
color: rgba(0,0,0,0.8);
padding: 8px;
}
/deep/ .el-input__inner{
border-color: #409eff;
}
.scrolling-menu{
border-top: none !important;
padding-top: 0 !important;
padding-bottom: 0 !important;
display: block;
position: static;
overflow-y: auto;
border: none;
-webkit-box-shadow: none !important;
box-shadow: none !important;
border-radius: 0 !important;
margin: 0 !important;
min-width: 100% !important;
width: auto !important;
border-top: 1px solid rgba(34,36,38,0.15);
}
.item-text{
border-top: none;
padding-right: calc(1.14285714rem + 17px ) !important;
line-height: 1.333;
padding-top: 0.7142857rem !important;
padding-bottom: 0.7142857rem !important;
position: relative;
cursor: pointer;
display: block;
border: none;
height: auto;
text-align: left;
border-top: none;
line-height: 1em;
color: rgba(0,0,0,0.87);
padding: 0.78571429rem 1.14285714rem !important;
font-size: 1rem;
text-transform: none;
font-weight: normal;
-webkit-box-shadow: none;
box-shadow: none;
-webkit-touch-callout: none;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
display: -webkit-box !important;
display: -ms-flexbox !important;
display: flex !important;
}
.icon-wrapper{
text-align: left;
width: 24px;
height: 20px;
-ms-flex-negative: 0;
flex-shrink: 0;
}
.text{
max-width: 80%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 12px;
font-weight: 400;
color: #40485b;
}
.addition{
background: #f6f6f6;
}
.user-add-label-text{
max-width: 80%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin: 0 4px;
}
</style>

+ 255
- 214
web_src/js/index.js View File

@@ -36,6 +36,7 @@ import MinioUploader from './components/MinioUploader.vue';
import ObsUploader from './components/ObsUploader.vue';
import EditAboutInfo from './components/EditAboutInfo.vue';
import Images from './components/Images.vue'
import EditTopics from './components/EditTopics.vue'

Vue.use(ElementUI);
Vue.prototype.$axios = axios;
@@ -2967,11 +2968,13 @@ $(document).ready(async () => {
initVueUploader();
initObsUploader();
initVueEditAbout();
initVueEditTopic();
initVueImages();
initTeamSettings();
initCtrlEnterSubmit();
initNavbarContentToggle();
initTopicbar();
// initTopicbar();
// closeTopicbar();
initU2FAuth();
initU2FRegister();
initIssueList();
@@ -3666,7 +3669,19 @@ function initVueEditAbout() {
});
}


function initVueEditTopic() {
const el = document.getElementById('topic_edit1');
if (!el) {
return;
}
new Vue({
el:'#topic_edit1',
render:h=>h(EditTopics)
})
}
function initVueImages() {
const el = document.getElementById('images');
console.log("el",el)
@@ -3677,6 +3692,7 @@ function initVueImages() {

new Vue({
el: '#images',
render: h => h(Images)
});
}
@@ -3932,218 +3948,243 @@ function initNavbarContentToggle() {
});
}

function initTopicbar() {
const mgrBtn = $('#manage_topic');
const editDiv = $('#topic_edit');
const viewDiv = $('#repo-topics');
const saveBtn = $('#save_topic');
const topicDropdown = $('#topic_edit .dropdown');
const topicForm = $('#topic_edit.ui.form');
const topicPrompts = getPrompts();

mgrBtn.on('click', () => {
viewDiv.hide();
editDiv.css('display', ''); // show Semantic UI Grid
});

function getPrompts() {
const hidePrompt = $('div.hide#validate_prompt');
const prompts = {
countPrompt: hidePrompt.children('#count_prompt').text(),
formatPrompt: hidePrompt.children('#format_prompt').text()
};
hidePrompt.remove();
return prompts;
}

saveBtn.on('click', () => {
const topics = $('input[name=topics]').val();

$.post(
saveBtn.data('link'),
{
_csrf: csrf,
topics
},
(_data, _textStatus, xhr) => {
if (xhr.responseJSON.status === 'ok') {
viewDiv.children('.topic').remove();
if (topics.length) {
const topicArray = topics.split(',');

const last = viewDiv.children('a').last();
for (let i = 0; i < topicArray.length; i++) {
const link = $('<a class="ui repo-topic small label topic"></a>');
link.attr(
'href',
`${AppSubUrl}/explore/repos?q=${encodeURIComponent(
topicArray[i]
)}&topic=1`
);
link.text(topicArray[i]);
link.insertBefore(last);
}
}
editDiv.css('display', 'none');
viewDiv.show();
}
}
)
.fail((xhr) => {
if (xhr.status === 422) {
if (xhr.responseJSON.invalidTopics.length > 0) {
topicPrompts.formatPrompt = xhr.responseJSON.message;

const {invalidTopics} = xhr.responseJSON;
const topicLables = topicDropdown.children('a.ui.label');

topics.split(',').forEach((value, index) => {
for (let i = 0; i < invalidTopics.length; i++) {
if (invalidTopics[i] === value) {
topicLables
.eq(index)
.removeClass('green')
.addClass('red');
}
}
});
} else {
topicPrompts.countPrompt = xhr.responseJSON.message;
}
}
})
.always(() => {
topicForm.form('validate form');
});
});

topicDropdown.dropdown({
allowAdditions: true,
forceSelection: false,
fields: {name: 'description', value: 'data-value'},
saveRemoteData: false,
label: {
transition: 'horizontal flip',
duration: 200,
variation: false,
blue: true,
basic: true
},
className: {
label: 'ui small label'
},
apiSettings: {
url: `${AppSubUrl}/api/v1/topics/search?q={query}`,
throttle: 500,
cache: false,
onResponse(res) {
const formattedResponse = {
success: false,
results: []
};
const stripTags = function (text) {
return text.replace(/<[^>]*>?/gm, '');
};

const query = stripTags(this.urlData.query.trim());
let found_query = false;
const current_topics = [];
topicDropdown
.find('div.label.visible.topic,a.label.visible')
.each((_, e) => {
current_topics.push(e.dataset.value);
});

if (res.topics) {
let found = false;
for (let i = 0; i < res.topics.length; i++) {
// skip currently added tags
if (current_topics.includes(res.topics[i].topic_name)) {
continue;
}

if (
res.topics[i].topic_name.toLowerCase() === query.toLowerCase()
) {
found_query = true;
}
formattedResponse.results.push({
description: res.topics[i].topic_name,
'data-value': res.topics[i].topic_name
});
found = true;
}
formattedResponse.success = found;
}

if (query.length > 0 && !found_query) {
formattedResponse.success = true;
formattedResponse.results.unshift({
description: query,
'data-value': query
});
} else if (query.length > 0 && found_query) {
formattedResponse.results.sort((a, b) => {
if (a.description.toLowerCase() === query.toLowerCase()) return -1;
if (b.description.toLowerCase() === query.toLowerCase()) return 1;
if (a.description > b.description) return -1;
if (a.description < b.description) return 1;
return 0;
});
}

return formattedResponse;
}
},
onLabelCreate(value) {
value = value.toLowerCase().trim();
this.attr('data-value', value)
.contents()
.first()
.replaceWith(value);
return $(this);
},
onAdd(addedValue, _addedText, $addedChoice) {
addedValue = addedValue.toLowerCase().trim();
$($addedChoice).attr('data-value', addedValue);
$($addedChoice).attr('data-text', addedValue);
}
});

$.fn.form.settings.rules.validateTopic = function (_values, regExp) {
const topics = topicDropdown.children('a.ui.label');
const status =
topics.length === 0 || (topics.last().attr('data-value').match(regExp) !== null && topics.last().attr('data-value').length <= 35);
if (!status) {
topics
.last()
.removeClass('green')
.addClass('red');
}
return status && topicDropdown.children('a.ui.label.red').length === 0;
};

topicForm.form({
on: 'change',
inline: true,
fields: {
topics: {
identifier: 'topics',
rules: [
{
type: 'validateTopic',
value: /^[\u4e00-\u9fa5a-z0-9][\u4e00-\u9fa5a-z0-9-]{0,105}$/,
prompt: topicPrompts.formatPrompt
},
{
type: 'maxCount[25]',
prompt: topicPrompts.countPrompt
}
]
}
}
});
}
// function initTopicbar() {
// const mgrBtn = $('#manage_topic');
// const editDiv = $('#topic_edit');
// const viewDiv = $('#repo-topics');
// const saveBtn = $('#save_topic');
// const topicDropdown = $('#topic_edit .dropdown');
// const topicForm = $('#topic_edit.ui.form');
// const topicInput = $("#topics_input")
// const topicPrompts = getPrompts();
// mgrBtn.on('click', (e) => {
// // viewDiv.hide();
// editDiv.css('display', ''); // show Semantic UI Grid
// topicInput.val('')
// console.log("-----------------asdasd",$("#topics_input"),$("#topics_input").val())
// stopPropagation(e);
// });
// $(document).bind('click',function(){
// editDiv.css('display','none');

// })
// editDiv.click(function(e){
// stopPropagation(e);
// })

// function getPrompts() {
// const hidePrompt = $('div.hide#validate_prompt');
// const prompts = {
// countPrompt: hidePrompt.children('#count_prompt').text(),
// formatPrompt: hidePrompt.children('#format_prompt').text()
// };
// hidePrompt.remove();
// return prompts;
// }

// function stopPropagation(e) {
// var ev = e || window.event;
// if (ev.stopPropagation) {
// ev.stopPropagation();
// }
// else if (window.event) {
// window.event.cancelBubble = true;//兼容IE
// }
// }


// saveBtn.on('click', () => {
// const topics = $('input[name=topics]').val();

// $.post(
// saveBtn.data('link'),
// {
// _csrf: csrf,
// topics
// },
// (_data, _textStatus, xhr) => {
// if (xhr.responseJSON.status === 'ok') {
// console.log("--------saveBtn------------")
// viewDiv.children('.topic').remove();
// if (topics.length) {
// const topicArray = topics.split(',');

// const last = viewDiv.children('a').last();
// for (let i = 0; i < topicArray.length; i++) {
// const link = $('<a class="ui repo-topic small label topic"></a>');
// link.attr(
// 'href',
// `${AppSubUrl}/explore/repos?q=${encodeURIComponent(
// topicArray[i]
// )}&topic=1`
// );
// link.text(topicArray[i]);
// link.insertBefore(last);
// }
// }
// editDiv.css('display', 'none');
// viewDiv.show();
// }
// }
// )
// .fail((xhr) => {
// if (xhr.status === 422) {
// if (xhr.responseJSON.invalidTopics.length > 0) {
// topicPrompts.formatPrompt = xhr.responseJSON.message;

// const {invalidTopics} = xhr.responseJSON;
// const topicLables = topicDropdown.children('a.ui.label');

// topics.split(',').forEach((value, index) => {
// for (let i = 0; i < invalidTopics.length; i++) {
// if (invalidTopics[i] === value) {
// topicLables
// .eq(index)
// .removeClass('green')
// .addClass('red');
// }
// }
// });
// } else {
// topicPrompts.countPrompt = xhr.responseJSON.message;
// }
// }
// })
// .always(() => {
// topicForm.form('validate form');
// });
// });

// topicDropdown.dropdown({
// allowAdditions: true,
// forceSelection: false,
// fields: {name: 'description', value: 'data-value'},
// saveRemoteData: false,
// label: {
// transition: 'horizontal flip',
// duration: 200,
// variation: false,
// blue: true,
// basic: true
// },
// className: {
// label: 'ui small label'
// },
// apiSettings: {
// url: `${AppSubUrl}/api/v1/topics/search?q={query}`,
// throttle: 500,
// cache: false,
// onResponse(res) {
// const formattedResponse = {
// success: false,
// results: []
// };
// const stripTags = function (text) {
// return text.replace(/<[^>]*>?/gm, '');
// };

// const query = stripTags(this.urlData.query.trim());
// let found_query = false;
// const current_topics = [];
// topicDropdown
// .find('div.label.visible.topic,a.label.visible')
// .each((_, e) => {
// current_topics.push(e.dataset.value);
// });

// if (res.topics) {
// let found = false;
// for (let i = 0; i < res.topics.length; i++) {
// // skip currently added tags
// if (current_topics.includes(res.topics[i].topic_name)) {
// continue;
// }

// if (
// res.topics[i].topic_name.toLowerCase() === query.toLowerCase()
// ) {
// found_query = true;
// }
// formattedResponse.results.push({
// description: res.topics[i].topic_name,
// 'data-value': res.topics[i].topic_name
// });
// found = true;
// }
// formattedResponse.success = found;
// }

// if (query.length > 0 && !found_query) {
// formattedResponse.success = true;
// formattedResponse.results.unshift({
// description: query,
// 'data-value': query
// });
// } else if (query.length > 0 && found_query) {
// formattedResponse.results.sort((a, b) => {
// if (a.description.toLowerCase() === query.toLowerCase()) return -1;
// if (b.description.toLowerCase() === query.toLowerCase()) return 1;
// if (a.description > b.description) return -1;
// if (a.description < b.description) return 1;
// return 0;
// });
// }

// return formattedResponse;
// }
// },
// onLabelCreate(value) {
// value = value.toLowerCase().trim();
// this.attr('data-value', value)
// .contents()
// .first()
// .replaceWith(value);
// return $(this);
// },
// onAdd(addedValue, _addedText, $addedChoice) {
// addedValue = addedValue.toLowerCase().trim();
// $($addedChoice).attr('data-value', addedValue);
// $($addedChoice).attr('data-text', addedValue);
// }
// });

// $.fn.form.settings.rules.validateTopic = function (_values, regExp) {
// const topics = topicDropdown.children('a.ui.label');
// const status =
// topics.length === 0 || (topics.last().attr('data-value').match(regExp) !== null && topics.last().attr('data-value').length <= 35);
// if (!status) {
// topics
// .last()
// .removeClass('green')
// .addClass('red');
// }
// return status && topicDropdown.children('a.ui.label.red').length === 0;
// };

// topicForm.form({
// on: 'change',
// inline: true,
// fields: {
// topics: {
// identifier: 'topics',
// rules: [
// {
// type: 'validateTopic',
// value: /^[\u4e00-\u9fa5a-z0-9][\u4e00-\u9fa5a-z0-9-]{0,105}$/,
// prompt: topicPrompts.formatPrompt
// },
// {
// type: 'maxCount[25]',
// prompt: topicPrompts.countPrompt
// }
// ]
// }
// }
// });
// }

window.toggleDeadlineForm = function () {
$('#deadlineForm').fadeToggle(150);


Loading…
Cancel
Save